正如标题所暗示的那样。我该怎么做呢?
我想在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!
答案 0 :(得分:335)
Array.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();
}
});
});
(感谢@vanuan和其他人)这种方法保证在调用“完成”回调之前处理所有项目。您需要使用在回调中更新的计数器。取决于索引参数的值不提供相同的保证,因为不保证异步操作的返回顺序。
(promise库可用于旧版浏览器):
处理所有保证同步执行的请求(例如1然后2然后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'))
处理所有异步请求而不执行“同步”(2可能比1快完成)
let requests = [1,2,3].map((item) => {
return new Promise((resolve) => {
asyncFunction(item, resolve);
});
})
Promise.all(requests).then(() => console.log('done'));
还有其他异步库,async是最受欢迎的,提供了表达你想要的机制。
编辑问题的正文已经过编辑,删除了以前的同步示例代码,所以我更新了我的答案以澄清。 原始示例使用同步代码来模拟异步行为,因此应用了以下内容:
array.forEach
是synchronous,res.write
也是 posts.foreach(function(v, i) {
res.write(v + ". index " + i);
});
res.end();
,因此您可以在致电foreach之后简单地进行回调:
{{1}}
答案 1 :(得分:22)
如果你遇到异步函数,并且你想确保在执行代码之前完成它的任务,我们总是可以使用回调功能。
例如:
var ctr = 0;
posts.forEach(function(element, index, array){
asynchronous(function(data){
ctr++;
if (ctr === array.length) {
functionAfterForEach();
}
})
});
注意:functionAfterForEach是在foreach任务完成后执行的函数。 asynchronous是在foreach中执行的异步函数。
希望这会有所帮助。
答案 2 :(得分:14)
奇怪的是,异步案件有多少错误答案! 可以简单地说明,检查索引不会提供预期的行为:
// 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.length - 1
,则在完成第一次迭代时将调用回调,而第一个元素仍处于未决状态!
为了在不使用异步等外部库的情况下解决这个问题,我认为最好的办法是保存列表的长度,并在每次迭代后减少。由于只有一个主题,我们确定没有竞争条件。
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);
});
答案 3 :(得分:10)
希望这可以解决您的问题,当我需要执行forEach内部的异步任务时,我通常会使用它。
foo = [a,b,c,d];
waiting = foo.length;
foo.forEach(function(entry){
doAsynchronousFunction(entry,finish) //call finish after each entry
}
function finish(){
waiting--;
if (waiting==0) {
//do your Job intended to be done after forEach is completed
}
}
带
function doAsynchronousFunction(entry,callback){
//asynchronousjob with entry
callback();
}
答案 4 :(得分:5)
通过ES2018,您可以使用异步迭代器:
const asyncFunction = a => fetch(a);
const itemDone = a => console.log(a);
async function example() {
const arrayOfFetchPromises = [1, 2, 3].map(asyncFunction);
for await (const item of arrayOfFetchPromises) {
itemDone(item);
}
console.log('All done');
}
答案 5 :(得分:2)
我没有承诺的解决方案(这确保每个动作在下一个动作开始之前结束):
Array.prototype.forEachAsync = function (callback, end) {
var self = this;
function task(index) {
var x = self[index];
if (index >= self.length) {
end()
}
else {
callback(self[index], index, self, function () {
task(index + 1);
});
}
}
task(0);
};
var i = 0;
var myArray = Array.apply(null, Array(10)).map(function(item) { return i++; });
console.log(JSON.stringify(myArray));
myArray.forEachAsync(function(item, index, arr, next){
setTimeout(function(){
$(".toto").append("<div>item index " + item + " done</div>");
console.log("action " + item + " done");
next();
}, 300);
}, function(){
$(".toto").append("<div>ALL ACTIONS ARE DONE</div>");
console.log("ALL ACTIONS ARE DONE");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="toto">
</div>
答案 6 :(得分:1)
var counter = 0;
var listArray = [0, 1, 2, 3, 4];
function callBack() {
if (listArray.length === counter) {
console.log('All Done')
}
};
listArray.forEach(function(element){
console.log(element);
counter = counter + 1;
callBack();
});
答案 7 :(得分:0)
答案 8 :(得分:0)
我的解决方案:
#Next-Level, #Last-Level {
width:100%;
height:100px;
position:fixed;
left:0px;
}
#Current-Level {
width:100%;
height:100px;
position:fixed;
top:150px;
}
#Next-Level {
top:0px;border-bottom:5px red dotted;
}
#Last-Level {
bottom:0px;border-top:5px blue dotted;
}
#Current-Level {
bottom:0px;border-top:5px green dotted;
}
示例:
<div id="Levels">
<div id="Last-Level"></div>
<div id="Current-Level"></div>
<div id="Next-Level"></div>
</div>
答案 9 :(得分:0)
我尝试使用Easy Way解决它,与您分享:
let counter = 0;
arr.forEach(async (item, index) => {
await request.query(item, (err, recordset) => {
if (err) console.log(err);
//do Somthings
counter++;
if(counter == tableCmd.length){
sql.close();
callback();
}
});
request
是节点js中mssql库的功能。这可以替换您想要的每个功能或代码。
好运
答案 10 :(得分:0)
from_fun
答案 11 :(得分:-1)
setInterval如何检查完整的迭代次数,带来保证。不确定它是否会超载范围,但我使用它,似乎是一个
_.forEach(actual_JSON, function (key, value) {
// run any action and push with each iteration
array.push(response.id)
});
setInterval(function(){
if(array.length > 300) {
callback()
}
}, 100);
答案 12 :(得分:-1)
一个简单的解决方案就像关注
function callback(){console.log("i am done");}
["a", "b", "c"].forEach(function(item, index, array){
//code here
if(i == array.length -1)
callback()
}
答案 13 :(得分:-2)
您不需要回调来遍历列表。只需在循环后添加end()
调用。
posts.forEach(function(v, i){
res.write(v + ". Index " + i);
});
res.end();