我遇到了一些问题。这是代码:
情况A:
var foundRiders = [];
riders.forEach(function(rider){
Rider.findOne({_id: rider}, function(err, foundRider){
if(err){
console.log("program tried to look up rider for the forEach loop finalizing the results, but could not find");
} else {
foundRiders.push(foundRider);
console.log(foundRiders);
}
});
});
情况B
var foundRiders = [];
riders.forEach(function(rider){
Rider.findOne({_id: rider}, function(err, foundRider){
if(err){
console.log("program tried to look up rider for the forEach loop finalizing the results, but could not find");
} else {
foundRiders.push(foundRider);
}
});
});
console.log(foundRiders);
所以在情境A中,当我控制日志时,我得到的是,FoundRiders是一个充满对象的数组。在情况B中,当我把console.log放在循环外面时,我的roundRiders数组是完全空的......
怎么回事?
答案 0 :(得分:4)
正如其他人所说,您的数据库代码是异步的。这意味着循环内部的回调会在循环结束很久之后的某个时间调用。有多种方法可以为异步循环编程。在您的情况下,最好转移到数据库的promise接口,然后开始使用promises来协调多个数据库调用。你可以这样做:
Promise.all(riders.map(rider => {
return Rider.findOne({_id: rider}).exec();
})).then(foundRiders => {
// all found riders here
}).catch(err => {
// error here
});
这使用mongoose数据库的.exec()
接口来运行查询并返回一个promise。然后,riders.map() builds and returns an array of these promises. Then,
Promise.all()monitors all the promises in the array and calls
。then()when they are all done or
。catch()`当出现错误时。
如果你想忽略在数据库中找不到的任何车手,而不是因为错误而中止,那么你可以这样做:
Promise.all(riders.map(rider => {
return Rider.findOne({_id: rider}).exec().catch(err => {
// convert error to null result in resolved array
return null;
});
})).then(foundRiders => {
foundRiders = foundRiders.filter(rider => rider !== null);
console.log(founderRiders);
}).catch(err => {
// handle error here
});
为了帮助说明这里发生了什么,这是一种更老式的监控方式,当所有数据库回调完成时(使用手动计数器):
riders.forEach(function(rider){
let cntr = 0;
Rider.findOne({_id: rider}, function(err, foundRider){
++cntr;
if(err){
console.log("program tried to look up rider for the forEach loop finalizing the results, but could not find");
} else {
foundRiders.push(foundRider);
}
// if all DB requests are done here
if (cntr === riders.length) {
// put code here that wants to process the finished foundRiders
console.log(foundRiders);
}
});
});
维护计数器以跟踪多个异步请求的业务是Promise.all()
内置的。
上面的代码假定您要并行化代码并一起运行查询以节省时间。如果要序列化查询,那么可以在ES6中使用await
并使用for
循环使循环“等待”每个结果(这可能会减慢速度)。以下是您将如何做到这一点:
async function lookForRiders(riders) {
let foundRiders = [];
for (let rider of riders) {
try {
let found = await Rider.findOne({_id: rider}).exec();
foundRiders.push(found);
} catch(e) {
console.log(`did not find rider ${rider} in database`);
}
}
console.log(foundRiders);
return foundRiders;
}
lookForRiders(riders).then(foundRiders => {
// process results here
}).catch(err => {
// process error here
});
请注意,虽然这看起来像你可能在其他语言中使用的同步代码更多,但它仍然使用异步概念,lookForRiders()
函数仍然返回一个使用{{1 }}。这是Javascript中的一个新功能,它使某些类型的异步代码更容易编写。