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.allimmediately 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.allmight hide useful information if multiple promises fail, since you only see the first rejection.Promise.allSettledgives 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.allSettledmay 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.allif you want all results or nothing. - Use
Promise.allSettledif you want a full picture and can handle partial failures. - Use
Promise.racefor timeouts or first response wins strategies. - Use
Promise.anyif 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 ()