According to MDN:
If any of the passed in promises rejects, the all Promise immediately rejects with the value of the promise that rejected, discarding all the other promises whether or not they have resolved.
The ES6 spec seems to confirm this.
My question is: Why does Promise.all
discard promises if any of them reject, since I would expect it to wait for "all" promises to settle, and what exactly does "discard" mean? (It's hard to tell what "discard" means for in-flight promises vs. promises that may not have run yet.)
I ask because I frequently run into situations where I have a list of promises and want to wait for them all to settle and get all rejections that may have occurred, which Promise.all
doesn't cater to. Instead, I have to use a hack like this:
const promises = []; // Array of promises
const settle = promise => promise.then(result => ({ result }), reason => ({ reason }));
Promise.all(promises.map(settle))
.then(/ * check "reason" property in each element for rejection */);
It resolves once all promises in the array resolve, or reject as soon as one of them rejects. In other words, it either resolves with an array of all resolved values, or rejects with a single error. Some libraries have something called Promise.
Promises cannot "be executed". They start their task when they are being created - they represent the results only - and you are executing everything in parallel even before passing them to Promise. all . Promises are executed at the moment of creation.
A Promise rejection indicates that something went wrong while executing a Promise or an async function. Rejections can occur in several situations: throwing inside an async function or a Promise executor/then/catch/finally callback, when calling the reject callback of an executor , or when calling Promise.
Assuming we have the processing power and that our promises can run in parallel, there is a hard limit of just over 2 million promises.
JavaScript | Promise.all () Method. The Promise.all () method is actually a promise that takes an array of promises (an iterable) as an input. It returns a single Promise that resolves when all of the promises passed as an iterable, which have resolved or when the iterable contains no promises. In simple way, if any of the passed-in promises ...
In other words, if any promise fails to get executed, then Promise.all () method will return an error and it will not take into the account whether other promises are successfully fulfilled or not. Example 2: Here Promise.all resolves after 2000 ms and the output is shows as an array. Here, Promise.all is the order of the maintained promises.
The Promise.all () method returns a single Promise that resolves when all of the promises passed as an iterable have resolved or when the iterable contains no promises. It rejects with the reason of the first promise that rejects. There is no implied ordering in the execution of the array of Promises given.
That's how Promise.all is designed to work. If a single promise reject()'s, the entire method immediately fails. There are use cases where one might want to have the Promise.all allowing for promises to fail. To make this happen, simply don't use any reject() statements in your promise.
The asynchronous operations associated with the promises are all run. If one of those promises rejects, then Promise.all()
simply does not wait for all of them to complete, it rejects when the first promise rejects. That is just how it was designed to work. If you need different logic (like you want to wait for all of them to be done, no matter whether they fulfill or reject), then you can't use just Promise.all()
.
Remember, a promise is not the async operation itself. A promise is just an object that keeps track of the state of the async operation. So, when you pass an array of promises to Promise.all()
, all those async operations have already been started and are all in-flight already. They won't be stopped or cancelled.
Why does Promise.all discard promises if any of them reject, since I would expect it to wait for "all" promises to settle.
It works the way it does because that's how it was designed and that is a very common use case when you don't want your code to continue if there was any sort of error. If it happens to not be your use case, then you need to use some implementation of .settle()
which has the behavior you want (which you seem to already know).
What I find the more interesting question is why is there not a .settle()
option in the specification and standard implementation since it is also a fairly common use case. Fortunately, as you have found, it is not a lot of code to make your own. When I don't need the actual reject reason and just want some indicator value to be placed into the array, I often use this fairly simple to use version:
// settle all promises. For rejeted promises, return a specific rejectVal that is
// distinguishable from your successful return values (often null or 0 or "" or {})
Promise.settleVal = function(rejectVal, promises) {
return Promise.all(promises.map(function(p) {
// make sure any values or foreign promises are wrapped in a promise
return Promise.resolve(p).catch(function(err) {
// instead of rejection, just return the rejectVal (often null or 0 or "" or {})
return rejectVal;
});
}));
};
// sample usage:
Promise.settleVal(null, someArrayOfPromises).then(function(results) {
results.forEach(function(r) {
// log successful ones
if (r !== null) {
console.log(r);
}
});
});
what exactly does "discard" mean?
It just means that the promises are no longer tracked by Promise.all()
. The async operations they are associated with keep right on doing whatever they were going to do. And, in fact if those promises have .then()
handlers on them, they will be called just as they normally would. discard
does seem like an unfortunate term to use here. Nothing happens other than Promise.all()
stops paying attention to them.
FYI, if I want a more robust version of .settle()
that keeps track of all results and reject reasons, then I use this:
// ES6 version of settle that returns an instanceof Error for promises that rejected
Promise.settle = function(promises) {
return Promise.all(promises.map(function(p) {
// make sure any values or foreign promises are wrapped in a promise
return Promise.resolve(p).catch(function(err) {
// make sure error is wrapped in Error object so we can reliably detect which promises rejected
if (err instanceof Error) {
return err;
} else {
var errObject = new Error();
errObject.rejectErr = err;
return errObject;
}
});
}));
}
// usage
Promise.settle(someArrayOfPromises).then(function(results) {
results.forEach(function(r) {
if (r instanceof Error) {
console.log("reject reason", r.rejectErr);
} else {
// fulfilled value
console.log("fulfilled value:", r);
}
});
});
This resolves to an array of results. If a result is instanceof Error, then it was a rejected, otherwise it's a fulfilled value.
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