How does Q.all work in NodeJS?

9.7k Views Asked by At

I have the following code:

var a = [1,2,3,4,5];
var promises = [];
a.forEach(function(item,index){
    var deferred = Q.defer();
    doSomething().then(function(){
        deferred.resolve(true);
        promises.push(deferred);
    });
});

Q.all(promises).then(function(data){
    console.log("something!!");
});

How does Q.all know that promises array has all the promises required by forEach loop? Sometimes, my Q.all runs before forEach. Please tell me where I am going wrong.

doSomething() is an asynchronous function which returns a promise.

2

There are 2 best solutions below

7
On BEST ANSWER

Q.all cannot run before your forEach, since forEach is synchronous.

But you actually push things in the array after Q.all has been called. Your way of using promises is a bit awkward : no need to use a deffered in a promise!

Plus, you don't want to push the deffered itself but the promise that it holds, after rejecting or resolving it. See below for more info on how to "promisify" an asynchronous funtion.

Deffered are used to define promises out of symple callback-based asynchronous code. Since doSomething() returns a promise (you are using .then()), you could actually just do :

var a = [1,2,3,4,5];
var promises = [];
a.forEach(function(item,index){
    var promise = doSomething().then(function(data){
        return Q(true);
    });
    promises.push(promise);
});

Q.all(promises).then(function(data){
    console.log("something!!");
});

Then promises will be directly populated with promises, without any delay.

EDIT : since you are asking about doSomething not being promise-enabled, here is what you could do:

Let's say doSomething takes as a parameter a callback to execute after some asynchronous task.

Then you could wrap doSomething that way:

function doSomethingPromise(){
    var defered = Q.defer();
    doSomething(function(err,data){
       if(err){
           defered.reject(err);
       }
       else{
           defered.resolve(data);
       }
    });
    return defered.promise;
}

and then use doSomethingPromise() as mentionned above, instead of doSomething, since this one returns a promise.

0
On

The problem is that when Q.all is executed, the promises array is still empty. Deferred objects are being pushed onto promises asynchronously, so Q.all() will be executed before the promise from doSomething() is resolved.