Angular $ q, How to link multiple promises inside and after a for loop

I want to have a for loop that calls asynchronous functions for each iteration.

After the for loop, I want to execute another block of code, but not earlier than all the previous calls in the for loop have been resolved.

My problem at the moment is that either the code block after the for loop is executed before all asynchronous calls are finished OR it is not executed at all.

Part of the code with a FOR loop and a block of code after it (for full code, see fiddle ):

[..] function outerFunction($q, $scope) { var defer = $q.defer(); readSome($q,$scope).then(function() { var promise = writeSome($q, $scope.testArray[0]) for (var i=1; i < $scope.testArray.length; i++) { promise = promise.then( angular.bind(null, writeSome, $q, $scope.testArray[i]) ); } // this must not be called before all calls in for-loop have finished promise = promise.then(function() { return writeSome($q, "finish").then(function() { console.log("resolve"); // resolving here after everything has been done, yey! defer.resolve(); }); }); }); return defer.promise; } 

I created a jsFiddle which can be found here http://jsfiddle.net/riemersebastian/B43u6/3/ .

At the moment, it looks like the execution order is in order (see console output).

I guess this is simply because every function call returns immediately without any real work. I tried deferring defer.resolve with setTimeout but failed (i.e. the last block of code never executed). You can see it in the broken block in the violin.

When I use real functions that write to and read from a file, the last block of code is executed until the last write operation is completed, which I don’t want.

Of course, the error may be in one of these read / write functions, but I would like to confirm that there is nothing wrong with the code that I posted here.

+73
angularjs promise deferred angular-promise
Jan 09 '14 at 15:31
source share
4 answers

What you need to use is $ q.all , which combines several promises into one, which is only allowed when all promises are.

In your case, you can do something like:

 function outerFunction() { var defer = $q.defer(); var promises = []; function lastTask(){ writeSome('finish').then( function(){ defer.resolve(); }); } angular.forEach( $scope.testArray, function(value){ promises.push(writeSome(value)); }); $q.all(promises).then(lastTask); return defer.promise; } 
+115
Jan 10 '14 at 23:28
source share

With the new ES7, you can get the same result in a much simpler way:

 let promises = angular.forEach( $scope.testArray, function(value){ writeSome(value); }); let results = await Promise.all(promises); console.log(results); 
+3
Jun 01 '15 at 8:29
source share

You can use $q and 'reduce' together to bind promises.

 function setAutoJoin() { var deferred = $q.defer(), data; var array = _.map(data, function(g){ return g.id; }); function waitTillAllCalls(arr) { return arr.reduce(function(deferred, email) { return somePromisingFnWhichReturnsDeferredPromise(email); }, deferred.resolve('done')); } waitTillAllCalls(array); return deferred.promise; } 
0
Apr 21 '17 at 13:12
source share

This worked for me using ES5 syntax

 function outerFunction(bookings) { var allDeferred = $q.defer(); var promises = []; lodash.map(bookings, function(booking) { var deferred = $q.defer(); var query = { _id: booking.product[0].id, populate: true } Stamplay.Object("product").get(query) .then(function(res) { booking.product[0] = res.data[0]; deferred.resolve(booking) }) .catch(function(err) { console.error(err); deferred.reject(err); }); promises.push(deferred.promise); }); $q.all(promises) .then(function(results) { allDeferred.resolve(results) }) .catch(function(err) { allDeferred.reject(results) }); return allDeferred.promise; } 
0
Nov 04 '17 at 1:04 on
source share



All Articles