可能重复:
javascript: execute a bunch of asynchronous method with one callback
我几天来一直在努力解决这个问题,但我无法想出一个优雅的方法来处理它。这是问题所在。
我正在运行forEach循环,我需要知道什么时候一切都已完成。由于forEach循环是阻塞的,因此只需在forEach循环之后删除console.log
就足够了,这将在forEach循环完成时运行。但是,如果forEach循环内部有任何不同步的函数,整个系统会很快崩溃,除了非常奇怪的hacky计数器和if语句之外,我无法知道循环的内容何时完成。我的问题是在javascript中是否有一种优雅的方式来处理这种情况。
以下是几个例子:
在这种情况下,forEach循环可以很好地工作,因为循环中的所有内容都是同步的。
[1,2,3].forEach(function(num){
console.log("we're on " + num);
});
console.log('done counting!')
//=> we're on 1
//=> we're on 2
//=> we're on 3
//=> done counting!
这是我们开始遇到问题的地方,作为一个基本的例子:
[1,2,3].forEach(function(num){
setTimeout(function(){ console.log(num) }, 200);
});
console.log('done counting!');
//=> done counting!
//=> we're on 1
//=> we're on 2
//=> we're on 3
虽然确实发生了这种情况,但我现在手上有一个不幸的问题,我需要回调一下,当我们完成计数时,除了像这样的东西之外我没有顺利的方法:
var counter = 0;
var numbers = [1,2,3]
var log_number = function(num){
console.log("we're on " + num);
counter++;
if (counter == numbers.length) {
console.log("done counting!")
}
}
numbers.forEach(function(num){
setTimeout(log_number(num), 200);
});
//=> we're on 1
//=> we're on 2
//=> we're on 3
//=> done counting!
善良,这是一个非常大的开销。有没有更顺畅的方法来实现这一目标?我已经检查了promises,deferred和sequence,但是我还没有真正能够以更简洁的方式实现其中的任何一个:(
要明确的是,这不是setTimeout特别是我的问题,它通常在同步函数中运行异步函数 - 这只是最简单的例子。
答案 0 :(得分:7)
Array.prototype.forEachDone = function(fn, scope, lastfn) {
for(var i = 0, c = 0, len = this.length; i < len; i++) {
fn.call(scope, this[i], i, this, function() {
++c === len && lastfn();
});
}
};
[1,2,3].forEachDone(function(num, i, arr, done){
setTimeout(function(){ console.log(num); done() }, 200);
}, this, function() {
console.log('done counting!');
});
这将做你想要的。 forEachDone将采用与forEach完全相同的参数,最后一个参数是在应用于数组的每个元素的函数完成时调用的回调。将应用于数组中每个元素的函数也采用与forEach完全相同的参数,但也使用另一个函数,该函数应在函数完成时调用。
PS:个人而言,我永远不会触及本机对象的原型,但如果你想要漂亮,那就是它。答案 1 :(得分:4)
var list = ["your", "mom"];
var count = 0;
for(var i = 0; i < list.length; ++i) {
getSomething(list[i], function() {
if(++count == list.length)
imDone();
});
}
另一种方式就是这样
var biscuits = ["cheddar", "butter milk"];
(function eatBiscuits(location) {
eatIt(biscuits[++location], whenAllEaten);
typeof biscuits[location] != "undefined" && eatBiscuits(location);
}(-1))
function eatIt(biscuit, cb) {
if (typeof biscuit == "undefined") {
cb();
} else {
stuffMyFace();
}
}
答案 2 :(得分:1)
如果您愿意使用lib,那么async将是一个不错的选择(适用于浏览器以及node.js)!
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);
答案 3 :(得分:1)
我倾向于使用这样的小实用功能:
function asyncCallback( iterations, onComplete ){
var total = iterations.length || iterations, count = 0;
return function( func ){
func();
if ( ++count == total ) onComplete();
};
}
var arr = [1,2,3];
var async = asyncCallback(arr, function(){
console.log('donezo!');
});
arr.forEach(function(num){
setTimeout(function(){
async(function(){
console.log(num);
});
}, 200);
});
答案 4 :(得分:0)
实际上没有'聪明'的方法来做到这一点。您需要使用setTimeout
,setInterval
,Worker
答案 5 :(得分:-1)
[1,2,3,4,5,6,7,8].forEach(function(n){
setTimeout(function(){
console.log(arguments[0]);
},1000*n,n);
});
这对我来说很好用