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.

Rob Kielty
  • 7,958
  • 8
  • 39
  • 51
  • its a promise. I can remove it, i was trying to solve the prob so i tried this thing – Muhammad Mudassir Sep 09 '14 at 19:00
  • 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? – Larry Battle Sep 09 '14 at 19:10
  • I think finalData is undefined because it's being access before it's populated from your deferred object. – Larry Battle Sep 09 '14 at 19:12
  • @MuddassirKhanani: Afair, `Q` has no `moulde` method. What do you mean? Where did you use it in your code? – Bergi Sep 09 '14 at 19:23

1 Answers1

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.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 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. – Muhammad Mudassir Sep 10 '14 at 14:50
  • 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); } } – Muhammad Mudassir Sep 10 '14 at 14:50
  • 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; }); } – Muhammad Mudassir Sep 10 '14 at 14:54
  • 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 `undefined`… – Bergi Sep 10 '14 at 14:54
  • I again have same issue this time one more loop is added @Bergi – Muhammad Mudassir Sep 13 '14 at 11:31
  • 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); } } – Muhammad Mudassir Sep 13 '14 at 11:34
  • function memberInfo(member){ var id = member return find({_id:member}) .then(function(User) { if (!User){ console.log(User)} else{ console.log(User)} return member; }); } – Muhammad Mudassir Sep 13 '14 at 11:34
  • var find = Q.nbind(User.findOne, User); – Muhammad Mudassir Sep 13 '14 at 11:35
  • I can't spot a mistake right away. If you still have an issue with it, you should [ask a new question](http://stackoverflow.com/questions/ask) where you can format your code readably. – Bergi Sep 13 '14 at 17:40
  • 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()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) – Nja Oct 22 '20 at 07:19