简体   繁体   中英

Array return null value with forEach loop in Nodejs and MongoDB

I am coding with Nodejs, MongoDB and Express. The code below looks for an user object with specific id in MongoDB. Once the user object is found, it retrieves favorite property of that object. The favorite property is an array whose each element is _id of a product. I tries to loop through this array. With each loop, I try to retrieve a product object from MongoDB and append this product object to new array (in my code below it's called "list"). I put some console.log() to check value of the list. It has value with each loop, but finally when I get the final one, it has null. I know the problem happens because I don't use properly deferred.resolve and deferred.promise. Please help me and explain deferred.resolve and deferred.promise works in the code. Thank you very much

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;
}

This is an asynchronous issue, but your forEach is useless because there is $in operator which will get all documents where the value of a field equals any value in the array :

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);
    }
})

The results is an array of products matching all the id in user.favorite array. If your items in user.favorite are not of type ObjectId, you may want to perform your mongo.helper.toObjectID(item) on each item before the query :

var favoriteArr = [];

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

Try This Your output is null becauase your foreach loop is async.. Please check below answer

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;
}

Modifications below demonstrates the concept of waiting to resolve the deferred promise until all the callbacks for retrieving product values have been made - the problem is an async issue.

There may be library routines which have the same functionality, there must be many ways of doing this, and be warned this code is untested:

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;
}

Note that promises can only be resolved or rejected once. So if one product retrieval fails and the promise is rejected, a later "resolve" of the promise because all callbacks have come in is ignored.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM