Noy Noy - 1 month ago 12
Node.js Question

Mongoose two request depending each other

I have images model and users model.
every image has a user_id field of a user and I want to get the picture of the user and name, add it to the image object and return an array of images.

When I am trying to add author_image field to ONE image I don't have any errors,
But when I am looping over all the images the app crashes the output is that imagesData is undefined as well as userData.
I tried using promises but again I get an error.
What is the best way I can do that without the undefined error?

router.route('/images/all')
.get(function(req,res){
var response = {};
var imagesData = {};
images.find({}).lean().exec(function(err,data){
// console.log(data);
imagesData = data;
if (!err) {
for (var i = 0; i < imagesData.length; i++) {
users.find(({'_id': imagesData[i].user_id}),function(err,userData){
console.log(userData);

imagesData[i].author_pic = userData[0].profile_image;
});
}

}
res.json(imagesData);

});

});

Answer

What you missed out is that find operation is not a synchronous operation. So all your find operation immediately move on to the next line.

Although there are multiple ways to handle such situation, I tend to use promises (Q library).

The code would look like this

var Q = require('q');

var defer = Q.defer();
images.find({}).lean().exec(function (err, data) {
    // console.log(data);
    imagesData = data;
    var promiseArr = [];
    if (!err) {
        for (var i = 0; i < imagesData.length; i++) {
            var innerDefer = Q.defer();
            users.find(({'_id': imagesData[i].user_id}), function (err, userData) {
                console.log(userData);
                defer.resolve(userData[0].profile_image);

            });
            promiseArr.push(innerDefer);
        }
    }
    Q.all(promiseArr).then(function (results) {
        for (var i = 0; i < imagesData.length; i++) {
            if (Q.isPromise(results[i])) {
                results[i] = results[i].valueOf();
            }
            imagesData[i].author_pic = results[i];
        }
        res.json(imagesData);
    })

});

In this case I am using the Q.all method which basically waits for all the find to finish, and executes only then.