Wait for all promises to resolve
Solution 1
I want the all to resolve when all the chains have been resolved.
Sure, then just pass the promise of each chain into the all()
instead of the initial promises:
$q.all([one.promise, two.promise, three.promise]).then(function() {
console.log("ALL INITIAL PROMISES RESOLVED");
});
var onechain = one.promise.then(success).then(success),
twochain = two.promise.then(success),
threechain = three.promise.then(success).then(success).then(success);
$q.all([onechain, twochain, threechain]).then(function() {
console.log("ALL PROMISES RESOLVED");
});
Solution 2
The accepted answer is correct. I would like to provide an example to elaborate it a bit to those who aren't familiar with promise
.
Example:
In my example, I need to replace the src
attributes of img
tags with different mirror urls if available before rendering the content.
var img_tags = content.querySelectorAll('img');
function checkMirrorAvailability(url) {
// blah blah
return promise;
}
function changeSrc(success, y, response) {
if (success === true) {
img_tags[y].setAttribute('src', response.mirror_url);
}
else {
console.log('No mirrors for: ' + img_tags[y].getAttribute('src'));
}
}
var promise_array = [];
for (var y = 0; y < img_tags.length; y++) {
var img_src = img_tags[y].getAttribute('src');
promise_array.push(
checkMirrorAvailability(img_src)
.then(
// a callback function only accept ONE argument.
// Here, we use `.bind` to pass additional arguments to the
// callback function (changeSrc).
// successCallback
changeSrc.bind(null, true, y),
// errorCallback
changeSrc.bind(null, false, y)
)
);
}
$q.all(promise_array)
.then(
function() {
console.log('all promises have returned with either success or failure!');
render(content);
}
// We don't need an errorCallback function here, because above we handled
// all errors.
);
Explanation:
From AngularJS docs:
The then
method:
then(successCallback, errorCallback, notifyCallback) – regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason.
$q.all(promises)
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
The promises
param can be an array of promises.
About bind()
, More info here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Solution 3
Recently had this problem but with unkown number of promises.Solved using jQuery.map().
function methodThatChainsPromises(args) {
//var args = [
// 'myArg1',
// 'myArg2',
// 'myArg3',
//];
var deferred = $q.defer();
var chain = args.map(methodThatTakeArgAndReturnsPromise);
$q.all(chain)
.then(function () {
$log.debug('All promises have been resolved.');
deferred.resolve();
})
.catch(function () {
$log.debug('One or more promises failed.');
deferred.reject();
});
return deferred.promise;
}
Related videos on Youtube
jensengar
Updated on March 01, 2020Comments
-
jensengar over 4 years
So I have a situation where I have multiple promise chains of an unknown length. I want some action to run when all the CHAINS have been processed. Is that even possible? Here is an example:
app.controller('MainCtrl', function($scope, $q, $timeout) { var one = $q.defer(); var two = $q.defer(); var three = $q.defer(); var all = $q.all([one.promise, two.promise, three.promise]); all.then(allSuccess); function success(data) { console.log(data); return data + "Chained"; } function allSuccess(){ console.log("ALL PROMISES RESOLVED") } one.promise.then(success).then(success); two.promise.then(success); three.promise.then(success).then(success).then(success); $timeout(function () { one.resolve("one done"); }, Math.random() * 1000); $timeout(function () { two.resolve("two done"); }, Math.random() * 1000); $timeout(function () { three.resolve("three done"); }, Math.random() * 1000); });
In this example, I set up a
$q.all()
for promises one, two, and three which will get resolved at some random time. I then add promises onto the ends of one and three. I want theall
to resolve when all the chains have been resolved. Here is the output when I run this code:one done one doneChained two done three done ALL PROMISES RESOLVED three doneChained three doneChainedChained
Is there a way to wait for the chains to resolve?
-
jensengar over 10 yearsThat requires me to know the length of my chain though right? I mean if I had a promise of length 10, I would have to do
$q.all([p1.then(..).then(...).then(...).then(...) ...]);
right? -
jensengar over 10 yearsThanks for confirming my worst fear. Now I have to come up with a way to get the last promise lol.
-
Bergi over 10 yearsWhat's the problem with that? Are your chains dynamically constructed?
-
jensengar over 10 yearsExactly my problem. I am trying to dynamically create a promise chain then I want to do something when the chain(s) complete.
-
Bergi over 10 yearsCan you show us your code (maybe ask a new question)? Are there items appended to the chain after
Q.all
was executed - otherwise it should be trivial? -
jensengar over 10 yearsI would love to show you the code... but I haven't finished writing it yet, however I will do my best to explain it. I have a list of "actions" that need to be done. These actions may have any number levels of sub-actions associated with them. I want to be able to do something when all the actions and their subactions are complete. There will likely be multiple
$q.all
s, however once I start the resolution process, no new actions/promises will be chained. -
Bergi over 10 yearsYeah, you'd recursively descend the tree of actions with multiple
all()
calls for subactions on each level - then you only need to callact(rootAction).then(…)
-
jensengar over 10 yearsExactly, I just haven't gotten that far yet or figured out how to build the
all()
correctly for each level. -
Bergi over 10 yearss/each level/each node/ - sorry if that has caused some confusion.
-
nick over 8 yearsThe
then
method of$q.all
is provided an array of the returned promises, so you can loop that array and callthen
on each item in the array, as opposed to callingthen
when you add the promise topromise_array
. -
Anastasia over 8 yearsIt's not jQuery.map() but Array.prototype.map() (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…) but this approach works.