JavaScript Promise: .all() vs .allSettled() and .race() vs .any()

shameel

Shameel Uddin

Posted on August 26, 2023

JavaScript Promise: .all() vs .allSettled() and .race() vs .any()

In previous articles we learned about how Promise work and discussed about then catch and finally methods.

Today we will be discussing:

  1. Promise.all()
  2. Promise.allSettled()
  3. Difference + Usecase of Promise.all() and Promise.allSettled()
  4. Promise.race()
  5. Promise.any()
  6. Difference + Usecase of Promise.race() and Promise.any()

All these methods take array of promises as an input but deals differently with respect to their behavior.

By mastering these methods, you'll have a versatile toolkit at your disposal, which will allow you to manage complex asynchronous operations.

Promise.all()

Promise.all method in JavaScript is used to handle multiple promises concurrently and wait for all of them to resolve.

ALL PROMISES MUST BE RESOLVED FOR IT TO RETURN A RESPONSE. Never forget that. =)

If all promises resolve then it returns an array with all resolved promises.

Let's take an example with the code:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 3 resolved"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.all(promisesArray)
  .then((results) => {
    console.log("All promises resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });
Enter fullscreen mode Exit fullscreen mode

In this snippet, we have three promises which resolve after 500, 800 and 1000 milliseconds. Since all of them are going to be resolved so you will see the log like this after you run this snippet:

All promises resolved: [ 'Promise 1 resolved', 'Promise 2 resolved', 'Promise 3 resolved' ]
Enter fullscreen mode Exit fullscreen mode

If any of the promise rejects, it won't wait for others to resolve and will return with reason of the promise that is rejected. Look at example below to get the better idea about this:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((_, reject) =>
  setTimeout(() => reject("Promise 3 rejected"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.all(promisesArray)
  .then((results) => {
    console.log("All promises resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });
Enter fullscreen mode Exit fullscreen mode

Here, promise2 resolves after 500 seconds but promise3 rejects after 800 seconds so it will not wait for promise1 and will return a response:

At least one promise rejected: Promise 3 rejected
Enter fullscreen mode Exit fullscreen mode

Promise.allSettled()

The Promise.allSettled method is used to handle multiple promises concurrently, just like Promise.all, but it waits for all the promises to settle (either resolve or reject) before proceeding. It returns an array of objects representing the outcomes of the input promises, including their values or reasons for rejection.

Promise.allSettled() always returns array of objects with status key which denotes fulfilled or rejected. If a promise is fulfilled then you can get response with value key and if the promise is rejected then you can find the reason in reason key.

Look at this code:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 3 resolved"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.allSettled(promisesArray).then((results) => {
  console.log("All promises settled:", results);
});
Enter fullscreen mode Exit fullscreen mode

You will see a response like this:

[
  { status: 'fulfilled', value: 'Promise 1 resolved' },
  { status: 'fulfilled', value: 'Promise 2 resolved' },
  { status: 'fulfilled', value: 'Promise 3 resolved' }
]
Enter fullscreen mode Exit fullscreen mode

Now we will try and reject promise2 like we did previously and see the response:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((_, reject) =>
  setTimeout(() => reject("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 3 resolved"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.allSettled(promisesArray).then((results) => {
  console.log("All promises settled:", results);
});
Enter fullscreen mode Exit fullscreen mode

Response:

[
  { status: 'fulfilled', value: 'Promise 1 resolved' },
  { status: 'rejected', reason: 'Promise 2 resolved' },
  { status: 'fulfilled', value: 'Promise 3 resolved' }
]
Enter fullscreen mode Exit fullscreen mode

Difference between Promise.all() and Promise.allSettled()

We have uncovered the difference of these two already with the coding approach; lets further theoretically discuss these

The Promise.all and Promise.allSettled methods are both used to work with multiple promises, but they have different behaviors when it comes to handling resolved and rejected promises. Here are the key differences between the two methods:

Handling Rejected Promises:

Promise.all: If any of the input promises is rejected, the entire returned promise will immediately reject with the reason of the first rejected promise. The resolution of Promise.all depends on all promises being fulfilled successfully.

Promise.allSettled: The returned promise always resolves, regardless of whether individual promises were fulfilled or rejected. The result is an array of objects representing the outcomes of all input promises, including both fulfilled and rejected ones.

Resolution Behavior:

Promise.all: Resolves only if all input promises are resolved successfully. The resolved value is an array containing the resolved values of the input promises in the same order.

Promise.allSettled: Resolves with an array of objects, one for each input promise. These objects contain the status of each promise ("fulfilled" or "rejected") along with the value (if resolved) or reason (if rejected).

Use Cases:

Promise.all: Useful when you want to wait for multiple asynchronous operations to complete and need all of them to be successful before proceeding. For example, making multiple API requests in parallel and waiting for all responses.

Promise.allSettled: Useful when you want to gather the results of multiple asynchronous operations, regardless of whether they succeeded or failed. It's often used when you want to know the outcome of all promises, even if some of them are rejected.

Promise.race()

As the name suggests, race returns first promise with shortest delay whether it is resolved or rejected.

For example if there are 5 promises which returns result like this:
Promise 1 ==> 1 second (rejected)
Promise 2 ==> 2 seconds (rejected)
Promise 3 ==> 3 seconds (resolved)
Promise 4 ==> 4 seconds (resolved)
Promise 5 ==> 5 seconds (resolved)

So, it will return us Promise 1 because it was the first one being returned.

A more formal way to put it is this:

The Promise.race method in JavaScript is used to handle multiple promises concurrently, but it resolves or rejects as soon as the first promise in the input array settles, either by resolving or rejecting.
This can be useful when you're interested in the result of the first promise to complete, regardless of whether it's a success or failure.

Lets look at this example where the first promise resolves:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 3 resolved"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.race(promisesArray)
  .then((results) => {
    console.log("First promise resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });
Enter fullscreen mode Exit fullscreen mode

You should see a response like this:

First promise resolved: Promise 2 resolved
Enter fullscreen mode Exit fullscreen mode

Lets look at this example where the first promise rejects:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((_,reject) =>
  setTimeout(() => reject("Promise 2 rejected"), 500)
);
const promise3 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 3 resolved"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.race(promisesArray)
  .then((results) => {
    console.log("First promise resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });
Enter fullscreen mode Exit fullscreen mode

You should see a response like this:

At least one promise rejected: Promise 2 rejected
Enter fullscreen mode Exit fullscreen mode

Please note: race does not care if all are resolved or all are rejected. It will give you first settled result whether it is resolved or rejected.

Promise.any()

It is somewhat similar to race method but with few minor differences:

  • It will return with first resolved promise.

For example if there are 5 promises which returns result like this:
Promise 1 ==> 1 second (rejected)
Promise 2 ==> 2 seconds (rejected)
Promise 3 ==> 3 seconds (resolved)
Promise 4 ==> 4 seconds (resolved)
Promise 5 ==> 5 seconds (resolved)

So, it will return us Promise 3 because it was the first one being resolved.

  • If all promises are rejected, it will give you an aggregated error.

For example if there are 5 promises which returns result like this:
Promise 1 ==> 1 second (rejected)
Promise 2 ==> 2 seconds (rejected)
Promise 3 ==> 3 seconds (rejected)
Promise 4 ==> 4 seconds (rejected)
Promise 5 ==> 5 seconds (rejected)

So, now it will return an aggregated result like this:

[AggregateError: All promises were rejected]
Enter fullscreen mode Exit fullscreen mode

Lets look at first example:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 2 resolved"), 500)
);
const promise3 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 3 resolved"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.any(promisesArray)
  .then((results) => {
    console.log("First promise resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });

Enter fullscreen mode Exit fullscreen mode

You will find a similar response as race method:

First promise resolved: Promise 2 resolved
Enter fullscreen mode Exit fullscreen mode

Lets look at another example:

const promise1 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 1 resolved"), 1000)
);
const promise2 = new Promise((_,reject) =>
  setTimeout(() => reject("Promise 2 rejected"), 500)
);
const promise3 = new Promise((resolve) =>
  setTimeout(() => resolve("Promise 3 resolved"), 800)
);

const promisesArray = [promise1, promise2, promise3];
Enter fullscreen mode Exit fullscreen mode

If you run this snippet in which the promise with shortest delay promise2 of 500ms is rejected and promise3 which has next shortest delay 800ms then:

  • race will return promise2 with rejected reason
  • any will return promise3 with resolved result.

Run it with promise.any:


Promise.any(promisesArray)
  .then((results) => {
    console.log("First promise resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });
Enter fullscreen mode Exit fullscreen mode

You will get this response:

First promise resolved: Promise 3 resolved
Enter fullscreen mode Exit fullscreen mode

Run it with promise.race:

Promise.race(promisesArray)
  .then((results) => {
    console.log("First promise resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });
Enter fullscreen mode Exit fullscreen mode

You will get this response:

At least one promise rejected: Promise 2 rejected
Enter fullscreen mode Exit fullscreen mode

Below is the case of all promises being rejected with promise.any:

const promise1 = new Promise((_, reject) =>
  setTimeout(() => reject("Promise 1 rejected"), 1000)
);
const promise2 = new Promise((_, reject) =>
  setTimeout(() => reject("Promise 2 rejected"), 500)
);
const promise3 = new Promise((_, reject) =>
  setTimeout(() => reject("Promise 3 rejected"), 800)
);

const promisesArray = [promise1, promise2, promise3];

Promise.any(promisesArray)
  .then((results) => {
    console.log("First promise resolved:", results);
  })
  .catch((error) => {
    console.error("At least one promise rejected:", error);
  });
Enter fullscreen mode Exit fullscreen mode

Then you will see a response like this:

At least one promise rejected: [AggregateError: All promises were rejected] {
  [errors]: [ 'Promise 1 rejected', 'Promise 2 rejected', 'Promise 3 rejected' ]
}
Enter fullscreen mode Exit fullscreen mode

In contrast, the same code with race method will return this:

At least one promise rejected: Promise 2 rejected
Enter fullscreen mode Exit fullscreen mode

Difference between Promise.race() and Promise.any()

We have already uncovered the differences of race and any in coding, lets further discuss them theoretically:

Both Promise.race and Promise.any are methods used to handle multiple promises concurrently, but they have different behaviors when it comes to resolving and rejecting. Here are the key differences between the two methods:

Resolution Behavior:

Promise.race: Resolves as soon as the first promise in the input array settles, whether it's resolved or rejected. The returned promise adopts the outcome (either resolve or reject) of the first settled promise.
Promise.any: Resolves as soon as any one of the input promises resolves. It doesn't matter if the other promises reject. The returned promise adopts the resolved value of the first resolved promise.

Rejection Handling:

Promise.race: If the promise that settles first is rejected, the returned promise will also reject with the same rejection reason.
Promise.any: If all input promises reject, the returned promise will reject with an aggregated error containing the reasons of all rejected promises. It only resolves if at least one promise resolves.

Use Cases:

Promise.race: Useful when you want to implement scenarios like a timeout mechanism where you want to respond to the first promise to complete, regardless of whether it succeeds or fails.
Promise.any: Useful when you want to handle the case where at least one promise out of multiple promises succeeds, and you're interested in the result of the first resolving promise.

Conclusion

So far, we have learned about Callback Hell and its solution as well. Then we explored promises in great detail and now we have learned about some of the important methods and their use-cases in real-life.

Follow me for more of such content:
LinkedIn: https://www.linkedin.com/in/shameeluddin/
Github: https://github.com/Shameel123

💖 💪 🙅 🚩
shameel
Shameel Uddin

Posted on August 26, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related