Mastering JavaScript Concurrency: Promise.all vs Promise.allSettled
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.
Comments ()