数组在Nodejs和MongoDB中用forEach循环返回null值

我使用Nodejs,MongoDB和Express编码。 下面的代码在MongoDB中查找具有特定ID的用户对象。 一旦find用户对象,它将检索该对象的collections属性。 favorite属性是一个数组,其每个元素都是一个产品的_id。 我试图循环这个数组。 每个循环,我尝试从MongoDB中检索一个产品对象,并将这个产品对象附加到新的数组(在我的代码下面,它被称为“列表”)。 我把一些console.log()来检查列表的值。 它对每个循环都有价值,但是最后当我得到最后一个时,它是空的。 我知道问题发生,因为我没有正确使用deferred.resolve和deferred.promise。 请帮我解释deferred.resolve和deferred.promise在代码中的作品。 非常感谢你

function showBasket(user) { var deferred = Q.defer(); var list =[]; db.users.findById(user, function (err, user) { if (err) deferred.reject(err); if (user) { var favorite = user.favorite; favorite.forEach(function(e){ db.products.findById(mongo.helper.toObjectID(e), function(err, product){ if (err) deferred.reject(err); if (product) { list.push(product); console.log(list);// list has value here } })//end db.products.findById })//end of forEach } //end of if console.log(list);// But finally, list has null value here deferred.resolve(list); });//end of db.users.findById return deferred.promise; } 

下面的修改表明等待解决延期承诺的概念,直到所有的检索产品价值的callback已经完成 – 问题是一个asynchronous的问题。

有可能是库函数具有相同的function,必须有很多方法来做到这一点,并警告这个代码是未经testing的:

 function showBasket(user) { var deferred = Q.defer(); var list =[]; db.users.findById(user, function (err, user) { if (err) deferred.reject(err); if (user) { var favorite = user.favorite; var callbacks = favorite.length; // how many call backs are expected if( callbacks == 0) { deferred.resolve( list); // or reject for reason "no products" return; } favorite.forEach(function(e){ db.products.findById(mongo.helper.toObjectID(e), function(err, product){ --callbacks; if (err) { deferred.reject(err); } else { if (product) { list.push(product); console.log(list);// list has value here } if( callbacks == 0) { console.log("all callbacks completed, resolve"); deferred.resolve(list); } } })//end db.products.findById })//end of forEach } //end of if });//end of db.users.findById return deferred.promise; } 

请注意,承诺只能解决或拒绝一次。 因此,如果一个产品检索失败,并且承诺被拒绝,那么由于所有callback进入而承诺的稍后“解决”将被忽略。

这是一个asynchronous的问题,但是你的forEach是没有用的,因为有$in操作符,它将得到一个字段的值等于数组中任何值的所有文档:

 db.products.find({ "_id": { "$in": user.favorite } }, function(err, products) { // here products is an array of product if (err) { console.log(err); deferred.reject(err); } else { console.log(products); deferred.resolve(products); } }) 

结果是一个匹配user.favorite数组中所有id的产品数组。 如果user.favorite中的项目不是ObjectIdtypes,则可能需要在查询之前对每个项目执行mongo.helper.toObjectID(item)

 var favoriteArr = []; for (var i = 0; i < user.favorite.length;i++)[ favoriteArr.push(mongo.helper.toObjectID(user.favorite[i])); } // use favoriteArr with $in operator 

试试这你的输出是null,因为你的foreach循环是asynchronous的..请检查下面的答案

 function showBasket(user) { var deferred = Q.defer(); var list = []; db.users.findById(user, function (err, user) { if (err) deferred.reject(err); if (user) { var favorite = user.favorite; // This function works like async loop (Recusrsive function) function uploader(i) { if (i < favorite.length) { db.products.findById(mongo.helper.toObjectID(e), function (err, product) { if (err) { console.log(err); deferred.reject(err) }; if (product) { list.push(product); console.log(list);// list has value here uploader(i + 1) } })//end db.products.findById } else { console.log(response); console.log(list);// This will be final result deferred.resolve(list); } } uploader(0) } //end of if });//end of db.users.findById return deferred.promise; }