ECMAScript 2020, the 11th edition, introduces the Promise.allSettled, a new Promise combinator that does not short-circuit; And Promise.any is scheduled to be supported in ES2021 standard. The two new features are inroduced five years after
Promise.all
andPromise.race
functions which were introduced in ES6(2015). The four combinator functions will make a more complete implementation ofPromise
for different asynchronous use cases.
Status of a Promise
A
settled
promise means that it is notpending
, i.e. it is eitherfulfilled
orrejected
.
Promise.all
The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.
Promise.all will shortcut on the first rejected promise, so we cannot get any resolved data/status once a promise is rejected even when some promises have already been resolved.
In order to get all the results(including status) of each promise after call Promise.all, we need to add a new function to return a new promise( promise.then() ), and return a new value with the status known in promise.then (either through the resolved branch or the rejected branch). Then iterate the results and filter the status after call Promise.all.
function reflect(promise) { return promise.then( (v) => { return { status: 'fulfilled', value: v }; }, (error) => { return { status: 'rejected', reason: error }; } ); } const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ]; const results = await Promise.all(promises.map(reflect)); const successfulPromises = results.filter(p => p.status === 'fulfilled');
Promise.allSettled
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ]; const results = await Promise.allSettled(promises); const successfulPromises = results.filter(p => p.status === 'fulfilled');
The similarities and diffrences with Promise.all
:
- They both receive an array of promises, and return a new promise;
Promise.allSettled
will not short-circuit on anyrejected
promise, it will return a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled(resolved or rejected); whilePromise.all
will short-circuit on the firstrejected
promise.
Obviously, Promise.allSettled
has some advantage as it can get all the state of all the settled promises.
const failedCount = results .filter(p => p.status === 'rejected').length if(failedCount > 0) { const errors = results .filter(p => p.status === 'rejected') .map(p => p.reason); }
The compatibility is good among mianstream browsers(only not fully supported on IE, firefox on Android and Samsung Internet).
Promise.any
The any function returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError holding the rejection reasons if all of the given promises are rejected.
The similarities and diffrences with Promise.all
:
- They both receive an array of promises, and return a new promise; they both have short-circuit behavior as well;
- They are fullfiled or rejected on opposite situations:
Promise.any
will short-circuit on the firstresolved
promise, whilePromise.all
will short-circuit on the firstrejected
promise;Promise.any
will be rejected when all the promises arerejected
, whilePromise.all
will be fullfiled when all promises areresolved
.
const promises = [ fetch('/endpoint-a').then(() => 'a'), fetch('/endpoint-b').then(() => 'b'), fetch('/endpoint-c').then(() => 'c'), ]; try { const first = await Promise.any(promises); // Any of the promises was fulfilled. console.log(first); // → e.g. 'b' } catch (error) { // All of the promises were rejected. console.assert(error instanceof AggregateError); // Log the rejection values: console.log(error.errors); }
Promise.any
is in the proposal stage 4 - the finished state in this July; and the Google v8(v8.5) engine has released support to this feature, it will be supported in the new Chrome version(v85).
However, the compatibility of browsers is cutrently not as good as Promose.allSettled
. We are expected to use this feature in 2021 or later.
Promise.race
The race function returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.
// example from mdn
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster
});
// expected output: "two"
Wrap-up
The table below shows the mian difference of these four main combinators in the Promise landscape.
function name | description | ES Version |
---|---|---|
Promise.race | short-circuits when an input value is settled | ES 2015 |
Promise.all | short-circuits when an input value is rejected | ES 2015 |
Promise.any | short-circuits when an input value is fullfiled | ES 2021 |
Promise.allSettled | does not short-circuit | ES 2020 |
Reference
- https://tc39.es/ecma262/#sec-promise.allsettled
- https://tc39.es/ecma262/#sec-promise.any
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
- https://github.com/tc39/proposals/blob/master/finished-proposals.md
- https://v8.dev/blog/v8-release-85
Comments