Issue in returning data retrieved from DB queries called in the loop
Asked Answered
S

1

18

I making multiple mongoDB queries in loop. and want to send the all results as one data array.But when I simple use the return for send the data it simply return undefined and do not wait for results of all DB request. I also tried to use q.moulde but same issue.

Code:

var getPrayerInCat = function(data){
    var result ;
    var finalData = [];
    if(data.length >0){
             data.forEach(function(data2){
                 var id= data2.id;
                 Prayer.find({prayerCat:id},function(err,prayer){
                     var deferred = Q.defer()
                     if (err) { // ...
                         console.log('An error has occurred');
                         // res.send(err);
                         result= finalData = err
                     } else {
                         if(!prayer){
                             // console.log(data2.id+'--0');
                             data2.prayersCount = 0;
                             result = deferred.resolve(finalData.push(data2))
                         } else {
                             // console.log(data2.id+'--'+prayer.length);
                             data2.prayersCount = prayer.length;
                             // console.log(prayer)
                             result =  deferred.resolve(finalData.push(data2))
                         } // else for data forward
                     }
                     deferred.promise;
                 })
                // deferred.resolve(finalData);

             })
             /*if(finalData.length > 0) { return finalData;}*/
        }
}

finalData is returned undefined.

Sibelius answered 9/9, 2014 at 18:53 Comment(4)
its a promise. I can remove it, i was trying to solve the prob so i tried this thingSibelius
Your function getPrayerInCat() doesn't return anything or return anything to a callback. Also it would be helpful if you removed the comments from your code and updated your post to include what your excepted output should be. And should you be return deferred.promise;, instead of just showing it?Scion
I think finalData is undefined because it's being access before it's populated from your deferred object.Scion
@MuddassirKhanani: Afair, Q has no moulde method. What do you mean? Where did you use it in your code?Keheley
K
21

Let's start with the general rule for using promises:

Every function that does something asynchronous must return a promise

Which functions are these in your case? It's getPrayerInCat, the forEach callback, and Prayer.find.

Hm, Prayer.find doesn't return a promise, and it's a library function so we cannot modify it. Rule 2 comes into play:

Create an immediate wrapper for every function that doesn't

In our case that's easy with Q's node-interfacing helpers:

var find = Q.nbind(Prayer.find, Prayer);

Now we have only promises around, and do no more need any deferreds. Third rule comes into play:

Everything that does something with an async result goes into a .then callback

…and returns the result. Hell, that result can even be a promise if "something" was asynchronous! With this, we can write the complete callback function:

function getPrayerCount(data2) {
    var id = data2.id;
    return find({prayerCat:id})
//  ^^^^^^ Rule 1
    .then(function(prayer) {
//  ^^^^^ Rule 3
        if (!prayer)
            data2.prayersCount = 0;
        else
            data2.prayersCount = prayer.length;
        return data2;
//      ^^^^^^ Rule 3b
    });
}

Now, we have something a bit more complicated: a loop. Repeatedly calling getPrayerCount() will get us multiple promises, whose asynchronous tasks run in parallel and resolve in unknown order. We want to wait for all of them - i.e. get a promise that resolves with all results when each of the tasks has finished.

For such complicated tasks, don't try to come up with your own solution:

Check the API of your library

And there we find Q.all, which does exactly this. Writing getPrayerInCat is a breeze now:

function getPrayerInCat(data) {
    var promises = data.map(getPrayerCount); // don't use forEach, we get something back
    return Q.all(promises);
//  ^^^^^^ Rule 1
}

If we needed to do anything with the array that Q.all resolves to, just apply Rule 3.

Keheley answered 10/9, 2014 at 2:48 Comment(10)
I have used your code but same issue it return form find and donot wait for resukt from db . So the final result is undefined.Sibelius
getPrayerCat('').then(function(data){ getPrayerInCat(data) }).then(function(data2){ console.log(data2) }) var getPrayerInCat = function(data){ var result ; var finalData = []; if(data.length >0){ var promises = data.map(getPrayerCount); return Q.all(promises); } }Sibelius
function getPrayerCount(data2) { var id = data2.id; return find({prayerCat:id}) .then(function(prayer) { if (!prayer) data2.prayersCount = 0; else data2.prayersCount = prayer.length; return data2; }); }Sibelius
You forgot about Rule 1 in your then callback. It needs to be getPrayerCat('').then(function(data){ return getPrayerInCat(data); }).then(…). Without returning anything, the promise resolves to undefinedKeheley
I again have same issue this time one more loop is added @KeheleySibelius
findAllGroup(query).then(function(data){ return getmemberInfo(data) }).then(function(data2){ res.send(data2) }) var getmemberInfo = function(data){ var result ; var finalData = []; if(data.length >0){ var promises = data.map(memberCount); return Q.all(promises); } } function memberCount(data2) { var members = (data2.members).split(",");; if(members.length >0){ var promises2 = members.map(memberInfo); return Q.all(promises2); } }Sibelius
function memberInfo(member){ var id = member return find({_id:member}) .then(function(User) { if (!User){ console.log(User)} else{ console.log(User)} return member; }); }Sibelius
var find = Q.nbind(User.findOne, User);Sibelius
I can't spot a mistake right away. If you still have an issue with it, you should ask a new question where you can format your code readably.Keheley
This is a really great answer, I would suggest to add a rule 4 or an example on how to handle multiple promises in case library don't do that. A reader may be interest in this: Promise.all()Heptad

© 2022 - 2024 — McMap. All rights reserved.