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 the all
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?
You can use the async/await syntax or call the . then() method on a promise to wait for it to resolve. Inside of functions marked with the async keyword, you can use await to wait for the promises to resolve before continuing to the next line of the function. Copied!
allSettled() will wait for all input promises to complete, regardless of whether or not one rejects. Consequently, it will always return the final result of every promise and function from the input iterable. Note: The order of the promise array is preserved upon completion of this method.
resolve() The Promise. resolve() method "resolves" a given value to a Promise . If the value is a promise, that promise is returned; if the value is a thenable, Promise. resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value.
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");
});
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
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With