如果我有一个传递了这个功能的功能:
function(work) {
work(10);
work(20);
work(30);
}
(可以有任意数量的work
来电,其中包含任意数字。)
work
表现出一些异步活动 - 比如,对于这个例子,它只是一个timeout
。我完全可以控制work
在完成此操作时所做的事情(事实上,它的定义一般来说)。
确定何时完成对work
的所有通话的最佳方式是什么?
当前方法在调用工作时递增计数器,在完成时递减计数器,并在计数器为0时触发all work done
事件(在每次递减后检查)。但是,我担心这可能是某种竞争条件。如果情况并非如此,请展示我的原因,这将是一个很好的答案。
答案 0 :(得分:2)
没有竞争条件。每个请求在完成时都要求执行递减(总是!包括http失败,这很容易忘记)。但这可以通过包装你的调用以更封装的方式处理。
未经测试,但这是要点(我已经实现了一个对象而不是一个计数器,所以理论上你可以扩展它以获得关于特定请求的更细粒度的查询):
var ajaxWrapper = (function() {
var id = 0, calls = {};
return {
makeRequest: function() {
$.post.apply($, arguments); // for example
calls[id] = true;
return id++;
},
finishRequest: function(id) {
delete calls[id];
},
isAllDone: function(){
var prop;
for(prop in calls) {
if(calls.hasOwnProperty(prop)) {return false;}
}
return true;
}
};
})();
用法:
而不是$.post("url", ... function(){ /*success*/ } ... );
我们会做
var requestId;
requestId = ajaxWrapper.makeRequest("url", ...
function(){ /*success*/ ajaxWrapper.finishRequest(requestId); } ... );
如果您想要更复杂,可以在包装器中自己添加finishRequest
的调用,因此使用几乎完全透明:
ajaxWrapper.makeRequest("url", ... function(){ /*success*/ } ... );
答案 1 :(得分:2)
您可以通过多种方式编写此程序,但使用计数器的简单方法可以正常工作。
要记住的重要一点是,这将起作用的原因是因为 Javascript在单个线程中执行。所有浏览器和node.js AFAIK都是如此。
基于下面的深思熟虑的评论,解决方案可行,因为JS事件循环将按以下顺序执行函数:
答案 2 :(得分:1)
我有一个after
效用函数。
var after = function _after(count, f) {
var c = 0, results = [];
return function _callback() {
switch (arguments.length) {
case 0: results.push(null); break;
case 1: results.push(arguments[0]); break;
default: results.push(Array.prototype.slice.call(arguments)); break;
}
if (++c === count) {
f.apply(this, results);
}
};
};
以下代码可行。因为javascript是单线程的。
function doWork(work) {
work(10);
work(20);
work(30);
}
WorkHandler(doWork);
function WorkHandler(cb) {
var counter = 0,
finish;
cb(function _work(item) {
counter++;
// somethingAsync calls `finish` when it's finished
somethingAsync(item, function _cb() {
finish()
});
});
finish = after(counter, function() {
console.log('work finished');
});
};
我想我应该解释一下。
我们将有效的函数传递给workhandler。
工作处理程序调用它并传入工作。
执行工作的函数调用多次递增计数器
由于有效的函数不是异步的(非常重要),我们可以在完成后定义完成函数。
正在完成的异步工作无法在当前同步工作块(整个工作处理程序的执行)完成之前完成(并调用未定义的完成函数)。
这意味着在整个workhandler完成后(并且设置了变量finish),异步工作作业将开始结束并调用finish。只有当所有人都呼叫完成后,回调才会发送到after
。