Marcell Ciszek Druzynski

Promise.allSettled()

A More Flexible Way to Handle Multiple Promises in JavaScript

JavaScript continues to evolve with exciting new features. While proposals like the pipe operator (similar to F#'s elegant function composition) are still in development, I want to highlight a powerful feature that's already available: Promise.allSettled().

When working with multiple promises, developers often reach for Promise.all(). However, this method takes an "all or nothing" approach. Let's understand why this might not always be ideal.

Understanding Promise.all()

Promise.all() works as follows:

  • Takes an array of promises and returns a new promise
  • Resolves only when ALL promises resolve successfully
  • Rejects immediately if ANY promise rejects
  • Returns an array of resolved values in the same order as input promises

Here's a practical example:

1const promise1 = Promise.resolve(3);
2const promise2 = Promise.resolve(42);
3const promise3 = Promise.reject("error");
4
5// All promises succeed
6Promise.all([promise1, promise2])
7 .then((values) => console.log(values)) // [3, 42]
8 .catch((error) => console.log(error));
9
10// One promise fails
11Promise.all([promise1, promise2, promise3])
12 .then((values) => console.log(values))
13 .catch((error) => console.log(error)); // 'error'
1const promise1 = Promise.resolve(3);
2const promise2 = Promise.resolve(42);
3const promise3 = Promise.reject("error");
4
5// All promises succeed
6Promise.all([promise1, promise2])
7 .then((values) => console.log(values)) // [3, 42]
8 .catch((error) => console.log(error));
9
10// One promise fails
11Promise.all([promise1, promise2, promise3])
12 .then((values) => console.log(values))
13 .catch((error) => console.log(error)); // 'error'

Promise.allSettled()

While Promise.all() is useful for scenarios where you need all promises to succeed, Promise.allSettled() takes a more forgiving approach. It waits for all promises to complete, regardless of whether they succeed or fail. This makes it particularly valuable when you need to:

  • Track the outcome of multiple independent operations
  • Handle partial successes gracefully
  • Collect both successful and failed results

Here's how Promise.allSettled() works:

1const promises = [
2 Promise.resolve("success"),
3 Promise.reject("failure"),
4 Promise.resolve("another success"),
5];
6
7Promise.allSettled(promises).then((results) => {
8 console.log(results);
9 /* Output:
10 [
11 { status: 'fulfilled', value: 'success' },
12 { status: 'rejected', reason: 'failure' },
13 { status: 'fulfilled', value: 'another success' }
14 ]
15 */
16});
1const promises = [
2 Promise.resolve("success"),
3 Promise.reject("failure"),
4 Promise.resolve("another success"),
5];
6
7Promise.allSettled(promises).then((results) => {
8 console.log(results);
9 /* Output:
10 [
11 { status: 'fulfilled', value: 'success' },
12 { status: 'rejected', reason: 'failure' },
13 { status: 'fulfilled', value: 'another success' }
14 ]
15 */
16});

The key difference is in the result format. Each promise outcome is represented by an object containing:

  • A status field: either 'fulfilled' or 'rejected'
  • A value field for successful promises
  • A reason field for rejected promises

Real-World Example

Let's look at a practical scenario where Promise.allSettled() shines, calling some user data APIs:

1async function fetchUserData(userIds) {
2 const userPromises = userIds.map((id) =>
3 fetch(`/api/users/${id}`)
4 .then((response) => response.json())
5 .catch((error) => error),
6 );
7
8 const results = await Promise.allSettled(userPromises);
9
10 const successfulFetches = results
11 .filter((result) => result.status === "fulfilled")
12 .map((result) => result.value);
13
14 const failedFetches = results
15 .filter((result) => result.status === "rejected")
16 .map((result) => result.reason);
17
18 return {
19 successful: successfulFetches,
20 failed: failedFetches,
21 };
22}
1async function fetchUserData(userIds) {
2 const userPromises = userIds.map((id) =>
3 fetch(`/api/users/${id}`)
4 .then((response) => response.json())
5 .catch((error) => error),
6 );
7
8 const results = await Promise.allSettled(userPromises);
9
10 const successfulFetches = results
11 .filter((result) => result.status === "fulfilled")
12 .map((result) => result.value);
13
14 const failedFetches = results
15 .filter((result) => result.status === "rejected")
16 .map((result) => result.reason);
17
18 return {
19 successful: successfulFetches,
20 failed: failedFetches,
21 };
22}

In this example, even if some API calls fail, we can still:

  • Process the successfully fetched user data
  • Log or handle the failed requests separately
  • Provide meaningful feedback about partial successes

Browser Support

Promise.allSettled() is well-supported in modern browsers and can be polyfilled for older environments. It's part of ES2020 and is available in:

  • Chrome 76+
  • Firefox 71+
  • Safari 13+
  • Edge 79+
  • Node.js 12.9.0+

But all depends when you are reading this blog post, so make sure to check the latest compatibility tables on MDN. Perhaps it is already available in all major browsers!

When to Use What

  • Use Promise.all() when:

    • You need all operations to succeed
    • A single failure should stop the entire process
    • You're dealing with dependent operations
  • Use Promise.allSettled() when:

    • You want to handle partial successes
    • Operations are independent of each other
    • You need detailed feedback about each promise's outcome

Summery

Use Promise.allSettled() when you need to handle multiple promises independently and want to track their outcomes, whether they succeed or fail. It's a powerful tool that provides more flexibility than Promise.all() in scenarios where partial successes are acceptable.

Additional Resources

  1. MDN Web Docs:

  2. JavaScript Specifications: