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

  1. apiOne resolves
  2. apiTwo rejects
  3. Promise.all rejects
  4. apiThree resolves
  5. 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.

Promise all explanation blog post image

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.

Promise allsettled explanation blog post image

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.

Anschrift
Am Hang 21, 58453 Witten
RKNN GmbH | 2011 - 2024