Mastering JavaScript Concurrency: Promise.all vs Promise.allSettled

Mastering JavaScript Concurrency: Promise.all vs Promise.allSettled
Photo by Shubham Dhage / Unsplash

JavaScript is designed to handle asynchronous operations gracefully. Whether you are fetching data from APIs, reading files, or executing time-consuming computations, Promises give you a structured way to manage asynchronous results.

Two of the most commonly used Promise combinators are Promise.all and Promise.allSettled. At first glance, they may seem similar, but their behavior can have very different implications for your applications. Let’s dive deep.


What is Promise.all?

Promise.all is best thought of as an all-or-nothing strategy.

  • If all promises succeed, you get a single resolved promise containing an array of results.
  • If any promise fails, the entire Promise.all immediately rejects, discarding results from other promises (even if they already resolved).

Example:

const fetchUser = fetch("/api/user");
const fetchOrders = fetch("/api/orders");
const fetchPayments = fetch("/api/payments");

Promise.all([fetchUser, fetchOrders, fetchPayments])
  .then(responses => {
    console.log("All succeeded:", responses);
  })
  .catch(error => {
    console.error("At least one failed:", error);
  });

Here, if /api/payments fails, you won’t get results from /api/user or /api/orders, even if they were successful.

When to use it:

  • When every result is mandatory for the next operation.
  • For example, loading multiple independent pieces of data before rendering a dashboard.

What is Promise.allSettled?

Promise.allSettled takes a more lenient approach.

  • It waits for all promises to finish, regardless of success or failure.
  • It always resolves (never rejects), returning an array of objects where each object indicates whether the corresponding promise fulfilled or rejected.

Example:

const fetchUser = fetch("/api/user");
const fetchOrders = fetch("/api/orders");
const fetchPayments = fetch("/api/payments");

Promise.allSettled([fetchUser, fetchOrders, fetchPayments])
  .then(results => {
    results.forEach(result => {
      if (result.status === "fulfilled") {
        console.log("Success:", result.value);
      } else {
        console.error("Failed:", result.reason);
      }
    });
  });

When to use it:

  • When partial success is acceptable.
  • For example, displaying available data to the user (orders and profile) even if one API (payments) fails.

Key Differences at a Glance

Feature Promise.all Promise.allSettled
Resolves when All promises fulfill All promises settle (fulfilled or rejected)
Rejects when Any single promise rejects Never rejects
Result Array of values Array of objects with status and value/reason
Use Case All results required Gather results regardless of failure

Real-World Considerations

1. Performance

Both methods execute promises concurrently, not sequentially. This means they are fast compared to awaiting each promise one by one.

2. Error Handling

  • With Promise.all, you must be prepared to catch a single error that cancels everything.
  • With Promise.allSettled, you must manually handle both success and failure cases in your loop.

3. Debugging

  • Promise.all might hide useful information if multiple promises fail, since you only see the first rejection.
  • Promise.allSettled gives you a complete report of successes and failures.

4. Browser Compatibility

  • Both methods are well-supported in modern browsers.
  • If you are targeting very old environments, Promise.allSettled may need a polyfill.

Beyond all and allSettled: Other Promise Combinators

To complete the picture, JavaScript also provides two more useful methods:

    • Returns the result of the first promise to settle (fulfilled or rejected).
    • Useful for timeouts or when you only care about the fastest response.
    • Returns the result of the first promise to fulfill (ignores rejections).
    • Rejects only if all promises reject.
    • Useful when one good result is enough (e.g., querying multiple redundant servers).

Promise.any

Promise.any([fetch("/mirror1"), fetch("/mirror2"), fetch("/mirror3")])
  .then(response => console.log("First successful response:", response))
  .catch(() => console.error("All requests failed"));

Promise.race

Promise.race([fetch("/api/fast"), fetch("/api/slow")])
  .then(result => console.log("Winner:", result))
  .catch(error => console.error("Error:", error));

Choosing the Right Tool

  • Use Promise.all if you want all results or nothing.
  • Use Promise.allSettled if you want a full picture and can handle partial failures.
  • Use Promise.race for timeouts or first response wins strategies.
  • Use Promise.any if you just need one successful result and don’t care which one.

Finally

Understanding the differences between Promise.all and Promise.allSettled (and their siblings race and any) is crucial for building robust, fault-tolerant, and performant JavaScript applications.

In real-world systems, APIs fail, networks drop, and data sources misbehave. Choosing the right promise combinator allows you to decide whether to fail fast, fail gracefully, or recover smartly.

Pro tip: Always consider the business logic of your application. If one missing dataset renders the whole page useless, Promise.all is your friend. But if you prefer resilience and “show what we can,” then Promise.allSettled shines.

Support Us