正如标题所示。我怎么做呢?
我想在foreach循环遍历每个元素并完成一些异步处理后调用whenAllDone()。
[1, 2, 3].forEach(
function(item, index, array, done) {
asyncFunction(item, function itemDone() {
console.log(item + " done");
done();
});
}, function allDone() {
console.log("All done");
whenAllDone();
}
);
有可能让它这样工作吗?当forEach的第二个参数是一个回调函数,当它经过所有迭代时运行?
预期的输出:
3 done
1 done
2 done
All done!
奇怪的是,异步情况下有多少错误的答案!
可以简单地表明,检查索引没有提供预期的行为:
// INCORRECT
var list = [4000, 2000];
list.forEach(function(l, index) {
console.log(l + ' started ...');
setTimeout(function() {
console.log(index + ': ' + l);
}, l);
});
输出:
4000 started
2000 started
1: 2000
0: 4000
如果我们检查index === array。长度- 1,回调将在第一次迭代完成时被调用,而第一个元素仍然悬而未决!
要在不使用async等外部库的情况下解决这个问题,我认为最好的方法是保存列表的长度,并在每次迭代后递减。因为只有一个线程,我们确定没有竞争条件的机会。
var list = [4000, 2000];
var counter = list.length;
list.forEach(function(l, index) {
console.log(l + ' started ...');
setTimeout(function() {
console.log(index + ': ' + l);
counter -= 1;
if ( counter === 0)
// call your callback here
}, l);
});
//First Example
save() {
this.holdImageData.forEach((val,i) => {
this.UploadToMinio(val.file, val.index, res => {
if (res && i+1 == this.holdImageData.length) {
this.FinallySave();
}
})
})
}
UploadToMinio(files, index, callback) {
this._fileUploadService.uploadFile(files[0], files[0].name, 'rms').subscribe(data => {
if (data) {
console.log('data >>> ', data);
callback(true);
}
})
}
FinallySave() {}
//Second Example
var sum = 0; // It can be global variable
startFunction() {
this.sumFunction(2, 4, res => {
if (res == true && sum == 6) {
this.saveFunction();
} else {
//call to another function
}
})
}
sumFunction(num1, num2, callback) {
if ((num1 + num2) == 6) {
callback(true)
}
callback(false);
}
数组中。forEach并没有提供这样的细节(如果可以的话),但是有几种方法可以实现你想要的:
使用一个简单的计数器
function callback () { console.log('all done'); }
var itemsProcessed = 0;
[1, 2, 3].forEach((item, index, array) => {
asyncFunction(item, () => {
itemsProcessed++;
if(itemsProcessed === array.length) {
callback();
}
});
});
这种方法保证在调用"done"回调之前处理所有项。您需要使用一个在回调中更新的计数器。依赖于索引参数的值并不能提供相同的保证,因为异步操作的返回顺序是不能保证的。
使用ES6承诺
(一个承诺库可以用于较旧的浏览器):
Process all requests guaranteeing synchronous execution (e.g. 1 then 2 then 3)
function asyncFunction (item, cb) {
setTimeout(() => {
console.log('done with', item);
cb();
}, 100);
}
let requests = [1, 2, 3].reduce((promiseChain, item) => {
return promiseChain.then(() => new Promise((resolve) => {
asyncFunction(item, resolve);
}));
}, Promise.resolve());
requests.then(() => console.log('done'))
Process all async requests without "synchronous" execution (2 may finish faster than 1)
let requests = [1,2,3].map((item) => {
return new Promise((resolve) => {
asyncFunction(item, resolve);
});
})
Promise.all(requests).then(() => console.log('done'));
使用异步库
还有其他异步库,async是最流行的,它们提供了表达所需内容的机制。
Edit
问题的主体已被编辑以删除之前的同步示例代码,因此我更新了我的答案以澄清。
最初的例子使用了类似于同步的代码来模拟异步行为,因此应用了以下内容:
数组中。forEach是同步的,res.write也是同步的,所以你可以简单地把你的回调放在你调用forEach之后:
posts.foreach(function(v, i) {
res.write(v + ". index " + i);
});
res.end();