Coding | JavaScript
Promise.allSettled vs Promise.all major differences by example.
We think it's great that Promise.allSettled
is available for developers by now already, it arrived with ECMAScript 2020. And because of that, we quickly want to share some examples showing the main differences between Promise.all
and Promise.allSettled
in everyday use for everyone asking himself one of the following questions
- How to wait for multiple promises to reject?
- How to make
Promise.all
continue even if one of the passed promises fails? - How is
Promise.allSettled
different from Promise.all? - What is
Promise.allSettled
even for?
Promise.all
waits for all Promises to be resolved or one of them to be rejected, Promise.allSettled
waits for all Promises to be resolved or rejected.
Comparing 'allSettled' and 'all' by example
ECMAScript 2020 provides us with a new feature method Promise.allSettled
which gives us more convenience with things that we could already accomplish before, but not so elegantly. So what does it do and what is it good for?
Promise.allSettled
takes in a list of promises and gives us a new promise, that resolves as soon as all of the input promises completed (no matter if they were resovled or rejected). So let's say we want to make two parallel API calls and wait for all of them to complete. Let's see how Promise.allSettled
can help us out.
Let's first fake some API calls by just creating three promises as if they would represent API requests. They need different amounts of time to complete and one of them even fails.
const apiOne = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('apiOne success')
resolve('apiOne success')
}, 100);
});
const apiTwo = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('apiTwo fail')
reject('apiTwo fail') // <-- notice the rejection
}, 200);
});
const apiThree = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('apiThree success')
resolve('apiThree success')
}, 300);
});
Imagine we are waiting for the result of that group of API responses. We could do that with Promise.all and with Promise.allSettled, right? Yes, but depending on what you want to achieve one of them will be the better fit. Let's look at what happens with those three fake API calls.
Promise.allSettled([apiOne, apiTwo, apiThree])
.then((result) => {
console.log('--> allSettled:', result);
});
Promise.all([apiOne, apiTwo, apiThree])
.then((result) => {
console.log('--> all success:', result);
})
.catch((err) => {
console.log('--> all failure:', err);
});
This will result in the following order of events
- apiOne resolves
- apiTwo rejects
Promise.all
rejects- apiThree resolves
Promise.allSettled
resolves
Here is the output of our code above
apiOne success
apiTwo fail
--> all failure: apiTwo fail
apiThree success
--> allSettled: [
{ status: 'fulfilled', value: 'apiOne success' },
{ status: 'rejected', reason: 'apiTwo fail' },
{ status: 'fulfilled', value: 'apiThree success' }
]
What Promise.all does here
Promise.all
waits for all promises to resolve and will then resolve itself. The important thing here is how rejections are being handled. When one of the passed promises rejects, the promise returned by Promise.all
will immediately reject without waiting any longer for promises that are still pending.
What Promise.allSettled does here
Promise.allSettled
is waiting for all promises to resolve or reject and will then resolve itself. When one of the passed promises rejects, Promise.allSettled
will continue to wait for all other promises.
Different result object structures
There are other differences, for example in the result we get. Promise.all gives us an array of values if all promises resolve and an error as soon as one of the promises rejects. Assuming all three promises would resolve in the example above, we would get
[ 'apiOne success', 'apiTwo success', 'apiThree success' ]
If there is a rejected promise, we get the rejection reason from just that one promise from the list that rejected first
'apiTwo fail'
Compared to that, Promise.allSettled
gives us an array of objects with a little more detailed information on what has happened and what where the reasons for failure. Like above, assuming all three promises are successful, we get 'status' and 'value' for each promise
[
{ status: 'fulfilled', value: 'apiOne success' },
{ status: 'fulfilled', value: 'apiTwo success' },
{ status: 'fulfilled', value: 'apiThree success' }
]
If there are rejected promises, the result object will hold 'status' and 'reason' of the rejection
[
{ status: 'fulfilled', value: 'apiOne success' },
{ status: 'rejected', reason: 'apiTwo fail' },
{ status: 'fulfilled', value: 'apiThree success' }
]
When to use what
That depends on what we want to achieve. If 'all' or 'allSettled' is the right choice will be determined by the underlying logic we intend to implement. When looking at what is right for a specific case there will usually be at least such other methods like Promise.race
and the also newly added Promise.any
that might also be what we need.
Mark Heyermann
Berater und Entwickler. Gründer und Geschäftsführer der RKNN GmbH.