【发布时间】:2015-01-28 21:42:15
【问题描述】:
在具有线程和锁的语言中,通过检查变量的值很容易实现延迟加载,如果它为空,则锁定下一段代码,再次检查值,然后加载资源并分配。这可以防止它被多次加载,并导致第一个线程之后的线程等待第一个线程完成所需的操作。
伪代码:
if(myvar == null) {
lock(obj) {
if(myvar == null) {
myvar = getData();
}
}
}
return myvar;
JavaScript 在单线程中运行,但是,由于在一个调用正在等待阻塞资源时异步执行,它仍然存在这种类型的问题。在这个 Node.js 示例中:
var allRecords;
module.exports = getAllRecords(callback) {
if(allRecords) {
return callback(null,allRecords);
}
db.getRecords({}, function(err, records) {
if (err) {
return callback(err);
}
// Use existing object if it has been
// set by another async request to this
// function
allRecords = allRecords || partners;
return callback(null, allRecords);
});
}
第一次调用此函数时,我会延迟加载小型数据库表中的所有记录,然后在后续调用中返回内存中的记录。
问题:如果同时向该函数发出多个异步请求,则该表将会从数据库中多次不必要地加载。
为了解决这个问题,我可以通过创建var lock; 变量并在表加载时将其设置为 true 来模拟锁定机制。然后,我会将其他异步调用放入 setTimeout() 循环中,并每隔(例如)1 秒检查一次该变量,直到数据可用,然后允许它们返回。
该解决方案的问题是:
- 它很脆弱,如果第一个异步调用抛出并且没有解除锁定怎么办。
- 在放弃之前,我们循环回到计时器多少次?
- 定时器应该设置多长时间?在某些环境中,1 秒可能太长且效率低下。
是否有在 JavaScript 中解决此问题的最佳实践?
【问题讨论】:
-
我会使用承诺。在第一次调用这个函数时,创建一个 Promise 并返回它。在随后的调用中,返回相同的承诺。有数据时解决。
-
@KevinB 如果您重新使用相同的 Promise 实例,如何将单独的回调保持直接?我猜 Promise 库只会处理多个
.then()调用并做正确的事情? (我没有写过很多基于 Promise 的代码,至少除了超级简单的东西之外没有太多。) -
这取决于实现(除了超简单的承诺之外,我还没有做太多事情)但是我很确定 .then() 如果从传入的返回任何内容,则返回一个新的承诺回调,意思是在这个 promise 上多次使用 .then 应该不会影响它的结果,除非你用
thepromise = thepromise.then(...覆盖它
标签: javascript multithreading asynchronous locking