Waiting on multiple promises as a group and singly in angular?

boatcoder picture boatcoder · Mar 28, 2014 · Viewed 8.9k times · Source

I'm using Restangular to do several simultaneous calls to the server. Restangular returns promises for each action, each one of them needs to do something with its specific return, and once all 3 are complete and their .then functions have completed, I need to perform another function.

Is this the right way to do promises in this instance?

var a, b, c;

a = service1.getList(...).then(function A(data) { do something; $log.debug('a'); return 'a';});
b = service2.getList(...).then(function B(data) { do something; $log.debug('b'); return 'b';});
c = service3.getList(...).then(function C(data) { do something; $log.debug('c'); return 'c';});

$q.all([a,b,c]).then(function wrapUp(values) { $log.debug(values); do something after functions A, B,and C above have finished;}

I don't know if $q.all() will run at the very end of A,B, and C, or if it could race in front of the last function. Will wrapUp() ALWAYS be called last?

Note the A,B, and C are the functions called after promises a,b, and c are completed.

The values array passed to wrapUp appears to be the the return values from A,B and C. However, in my console, functions A logs 10 times, and then function B logs 10 times, then function C and function wrapUp are both logging 10 times (alternating).

The 10x logging seems like something is broken....., and the feared race condition appears to be a possibility....

Can someone explain what is going on here?

Chrome Dev Console

Answer

Benjamin Gruenbaum picture Benjamin Gruenbaum · Mar 28, 2014

Is this the right way to do promises in this instance?

Yes, it is.

The 10x logging seems like something is broken....., and the feared race condition appears to be a possibility....

Can someone explain what is going on here?

It's actually not broken. Like you suggest $q.all waits for all promises to complete.

In:

 $q.all([a,b,c,d]).then(function(){
     // yes, this only runs after all of the promises fulfilled
 });

Here is what happens:

You have a loop that runs 10 times:

  • A calls from the entire loop all fulfill instantly.
  • B calls from the entire loop all fulfill instantly.
  • C call resolves
    • The promise composed of the $q.all for that instance of a,b,c for the a (already resolved) the b (already resolved) and now the c - fulfilled.
  • Next c call resolves
    • The promise composed of the $q.all for that instance of a,b,c for the a (already resolved) the b (already resolved) and now the c - fulfilled.

And so on.

The only guarantee you have is that inside the loop that instance of $q.all([a,b,c] will fulfill after its local a,b and c. Nothing else, nothing more.

If you want to hook on the aggregate promise of all the return values, you can of course perform a $q.all on all ten of them.