这个情况比较恶心,比如forEach() 然后这个里面每个Item都执行一个异步调用的函数,比如请求数据库查询结果,我希望在forEach之后的代码块能在上面每个Item的异步函数执行完之后再执行,请问有没有这样的库或者其他替代方案?
也就是先解决forEach的异步问题改成同步,然后forEach里面每个item的异步问题也改成同步,然后再执行forEach后面的代码块。不知道有没有描述清楚,我用过async那个库,看似能解决问题但是尝试了一下不好用…
我以前用过的一段类似代码,因为即使中间某一条有错也要全执行完,所以err没有往回传。如果callback中err有值循环的最后那个function会立刻执行。这个根据你自己需要改。
async.forEach(result, function(item,callback){
var parms = packParms(item);
api.inpatientPrescriptionPricing(parms,function(err,result) {
if(err){
console.error('inpatientPrescriptionPricing err:', parms);
db.log(item.id,item.zyh,item.xmmc,err.msg);
callback(null,result);
}else{
db.update(item, function (err,row) {
if(err){
console.error('update err: id = %d', item.id);
callback(null,item);
}else{
callback(null,item);
}
});
}
});
}, function(err,rs){
if(err&&err.length>0){
console.error('filterSeries err:',err);
console.error('filterSeries rs:',rs);
}
process.exit(1);
});
@saighost 这是我测试用的代码,我希望的效果是当forEach里面的异步函数都执行完毕后执行最后一个console.log,实际的结果是先执行完三个forEach,然后执行最下面那一行console.log,然后执行三个at的console,然后执行三个save的console。希望能在forEach,at,save都执行完事之后再执行最后的console.log
var ids = ["50514054705efe0823000008", "505140e060b550402400000c", "505c092e54f35e414c000008"]; async.forEach(ids, function(item){ console.log("forEach and id:"+item); user.at(item, function(err, u){ console.log("at and id:"+item); u.uid = "测试用"; u.save(function(err, u2){ console.log("save and id:"+item); }); }); }); console.log("after async.forEach");
@shinka 你应该先理解什么是异步回调,补一下基础。
var ids = ["50514054705efe0823000008", "505140e060b550402400000c", "505c092e54f35e414c000008"];
async.forEach(ids, function(item){
console.log("forEach and id:"+item);
user.at(item, function(err, u){
console.log("at and id:"+item);
u.uid = "测试用";
u.save(function(err, u2){
console.log("save and id:"+item);
callback(null);
});
});
},function(err,rs){
console.log("after async.forEach");
});
@saighost 嗯 谢谢哈 昨天已经解决了 不过遇到一个新的问题 请问我用mongoose的话,想用where a_ids in aArray 或者 b_ids in bArray 或者c_ids in cArray,我刚用$where试了下失败了,代码如下,希望帮忙看看怎么整比较好。
var line = "";
if(uids_.length > 0){ line += "createby.uid in [" + uids_ + "]"; } if(gids_.length > 0){ line += " || createby.gid in [" + gids_ + "]"; } if(tids_.length > 0){ line += " || createby.tid in [" + tids_ + "]"; }
message.find({}).$where(line).setOptions(optionObj).exec(function(err, messages){ callback_(err, messages); });
https://github.com/xinyu198736/queue_do 专门处理forEach列表里的异步转同步问题。
衍生项目(使用了queue_do的模块):
@saighost 老哥 我有个关于async的疑问 今天用你上面的例子试了一下,发现如下问题,先贴上代码
async.forEach(msgs, function(msg, cb){ async.parallel({ group: function(callback){ if(msg.createby.gid){ group.at(msg.createby.gid, function(err, g){ callback(err, g); }); }else{ callback(null); } }, user: function(callback){ if(msg.createby.uid){ user.at(msg.createby.uid, function(err, u){ callback(err, u); }); }else{ callback(null); } }, }, function(err, r){ if(r.group){ msg["group"] = r.group; } if(r.user){ msg["user"] = r.user; } cb(msg); }); }, function(arg1, arg2, arg3, arg4, arg5){ 1; });在对msgs这个数组forEach的时候,会每次调用回调,然后回调里面取得这条message的创建者以及所在群的对象信息添加到msg属性中。但是最后一个callback是在第一个item执行完之后就调用了,由cb(msg)这句调用了,然后forEach继续执行其他的item,最后那个回调就不再执行了。我以为最后一个回调是forEach所有元素都遍历结束后才调用的。请问如果用async这个模块的话,有没有解决方法。不用计数啥的。
@shinka cb的第一个参数应该是err,第二个是msg。所以改成cb(null,msg)就OK了,以后写的多了就习惯了。执行一次就不再执行了是因为forEach(arr, iterator, callback)在遍历的时候如果有一个报err的时候就会直接执行callback然后结束。我上面的代码为了执行全部所以,callback的第一个参数都是null然后用自己的log记录了一下,你可以根据自己情况(出错时是否继续执行)自行修改。 回调第一个参数是err好像是nodejs里约定俗称的,你自己写的代码最好也保持这种风格。
@saighost 嗯嗯 我一开始第一个参数也是err,后来发现不行就把err删了也没成功 复制代码的时候忘记恢复回去了 刚跟老大讨论了一下 决定用async的forEachSeries了 谢谢老哥的意见哈~