确定异步循环的结束
determining the end of an asynchronous loop
在所附的代码中,我希望在运行完所有数据库查询后运行函数returnFile
,但问题是我无法从查询响应中判断出哪个响应将是最后一个响应,所以我想的是将循环分开,只让最后一个回调运行returnFile
函数,但这会大大降低速度。
for (var i = 0, len = articleRevisionData.length; i < len; i++) {
tagNames=[]
console.log("step 1, "+articleRevisionData.length+" i:"+i);
if(articleRevisionData[i]["tags"]){
for (var x = 0, len2 = articleRevisionData[i]["tags"].length; x < len2; x++) {
console.log("step 2, I: "+i+" x: "+x+articleRevisionData[i]["articleID"])
tagData.find({"tagID":articleRevisionData[i]["tags"][x]}).toArray( function(iteration,len3,iterationA,error, resultC){
console.log("step 3, I: "+i+" x: "+x+" iteration: "+iteration+" len3: "+len3)
if(resultC.length>0){
tagNames.push(resultC[0]["tagName"]);
}
//console.log("iteration: "+iteration+" len: "+len3)
if(iteration+1==len3){
console.log("step 4, iterationA: "+iterationA+" I: "+iteration)
articleRevisionData[iterationA]["tags"]=tagNames.join(",");
}
}.bind(tagData,x,len2,i));
}
}
if(i==len-1){
templateData={
name:userData["firstName"]+" "+userData["lastName"],
articleData:articleData,
articleRevisionData:articleRevisionData
}
returnFile(res,"/usr/share/node/Admin/anonymousAttempt2/Admin/Articles/home.html",templateData);
}
}
从循环中调用异步函数很少是个好主意,因为正如您所发现的,您无法知道所有调用何时完成(这是异步的本质)
在您的示例中,需要注意的是,所有异步调用都是并发运行的,这可能会消耗比您希望的更多的系统资源。
我发现,解决这类问题的最佳方案是使用事件来管理执行流,如:
const EventEmitter = require('events');
const emitter = new EventEmitter();
let iterations = articleRevisionData.length;
// start up state
emitter.on('start', () => {
// do setup here
emitter.emit('next_iteration');
});
// loop state
emitter.on('next_iteration', () => {
if(iterations--) {
asyncFunc(args, (err,result) => {
if(err) {
emitter.emit('error', err);
return;
}
// do something with result
emitter.emit('next_iteration');
});
return;
}
// no more iterations
emitter.emit('complete');
});
// error state
emitter.on('error', (e) => {
console.error(`processing failed on iteration ${iterations+1}: ${e.toString()}`);
});
// processing complete state
emitter.on('complete', () => {
// do something with all results
console.log('all iterations complete');
});
// start processing
emitter.emit('start');
请注意,这段代码是多么简单和干净,没有任何"回调地狱",以及可视化程序流是多么容易。
还值得注意的是,您可以使用事件来表达每种类型的执行控制(doWhile、doUntil、map/reduce、队列工作者等),并且由于事件处理是Node的核心,因此您会发现以这种方式使用它们将优于大多数(如果不是所有的话)其他解决方案。
有关Node中事件处理的更多信息,请参见Node Events。
相关文章:
- 为什么“;未定义的“;在JavaScript中结束循环
- 循环结束/推送到数组之前在页面上呈现EJS
- 希望日期开始结束于while循环中的一个房间id的一个数组
- 使用while循环+break是一种可接受的方式;goto;手术的结束
- 用于循环的 Javascript 过早结束
- 如何在javascript中循环结束后执行语句
- 在for循环中另一个音频结束后播放一段时间的音频
- addclass循环结束时调用函数
- Sequelize等待,直到循环通过回调结束
- Node/Express - 如何等到 For 循环结束再使用 JSON 进行响应
- 如何使用事件处理程序连续循环遍历数组列表(从结束恢复到开始)
- 同时循环承诺,直到另一个承诺结束
- 破;不结束循环
- 因为循环过早结束
- jQuery函数使用包含setTimeout的每个循环调用自身,立即发生,而不是在结束时发生
- 在javascript中使用什么循环,在结束之前运行某个代码
- Javascript-在while循环结束后仅显示数组中的最后一个值
- 返回HTML结束循环过早
- 过期或超时在X秒后永不结束循环
- JS动画结束循环,希望在不使用的情况下连续循环;setInterval/setTimout