Asynchronous JavaScript
Promise Combinators
Master JavaScript Promise combinators. Compare Promise.all, Promise.allSettled, Promise.race, and Promise.any in terms of resolve/reject conditions.
1. Introduction
When managing multiple asynchronous tasks, executing them sequentially is often inefficient. JavaScript provides four static methods on the Promise constructor, known as Promise Combinators, to coordinate parallel asynchronous tasks.
2. Why It Matters
Choosing the correct combinator is critical to handling parallel tasks. For example, using Promise.all for independent tasks can cause the entire query to fail if a single request rejects, while using Promise.allSettled guarantees that you receive the results of all requests regardless of whether they succeed or fail.
3. Real-World Analogy
Think of a Group Vacation Trip:
- Promise.all (All or Nothing): A group of friends driving separate cars. They agree to meet at the hotel. If one car breaks down (rejects), the trip is cancelled for everyone.
- Promise.allSettled (Roll Call): The coordinator calls each driver to check their status: "Who arrived safely, and who had car trouble?" The trip continues, and you collect the status of each driver.
- Promise.race (Fastest Car Wins): A race where the first car to cross the finish line wins, regardless of whether it finishes successfully or crashes (rejects). The race ends as soon as the first result is determined.
- Promise.any (First Safe Arrival): You order a taxi from three different apps. You get into the first taxi that arrives (resolves) and cancel the other requests. You only care about the first successful arrival. If all three apps report no drivers available (all reject), the request fails.
4. The Four Combinators
Let's compare the behavior of the four static methods:
1. Promise.all:
Resolves only when all input promises resolve successfully. It rejects immediately if any input promise rejects (short-circuit behavior).
2. Promise.allSettled:
Waits until all input promises have settled (either resolved or rejected). It never rejects, returning an array of objects describing the outcome of each promise.
3. Promise.race:
Resolves or rejects as soon as the first input promise settles, passing its value or rejection reason forward.
4. Promise.any:
Resolves as soon as the first input promise resolves successfully. If all input promises reject, it rejects with an AggregateError containing all rejection reasons.
5. Comparison Matrix
| Combinator | Resolves When... | Rejects When... | Short-circuit Behavior? |
|---|---|---|---|
Promise.all |
All resolve successfully | Any single promise rejects | Yes (on first rejection) |
Promise.allSettled |
All settle (resolve or reject) | Never rejects | No |
Promise.race |
Any promise settles (first wins) | First settled promise rejects | Yes (on first settlement) |
Promise.any |
Any single promise resolves | All input promises reject | Yes (on first success) |
6. Practical Example
This script demonstrates using Promise.race to implement a network request timeout:
7. Common Mistakes
- Using Promise.all for independent UI modules: If you use
Promise.allto load dashboard widgets, a single widget load error crashes the entire dashboard. UsePromise.allSettledinstead to load remaining widgets successfully.
8. Quick Quiz
Q1: Which combinator rejects with an AggregateError only if all input promises reject?
A) Promise.race
B) Promise.any
Answer: B — Promise.any() resolves on the first successful promise, and rejects with an AggregateError if all promises fail.
9. Scenario-Based Challenge
The Multi-Region Mirror Request:
An application queries mirrors in different regions: US, EU, and ASIA. You want to retrieve data from the fastest server that responds successfully. Explain which combinator fits best and write the query wrapper.
10. Debugging Exercise
Explain why this Promise.all call rejects, and how to fix it:
const files = [ fetch('/file-a.json'), fetch('/file-invalid-path.json'), // fails with 404! fetch('/file-c.json') ];
Promise.all(files) .then(responses => console.log('Successfully fetched all files!')) .catch(err => console.error('Failed: ' + err.message)); // triggers error!
View Solution
Diagnosis: Since Promise.all uses all-or-nothing logic, a single rejection causes the entire call to reject, ignoring the successful fetches.
Fix: Switch to Promise.allSettled to inspect results individually, allowing the application to process the successful files:
Promise.allSettled(files)
.then(results => {
const loaded = results.filter(r => r.status === 'fulfilled');
console.log(`Successfully fetched ${loaded.length} files.`);
});
11. Interview Questions
🟢 Q1: Compare Promise.race and Promise.any in detail.
Answer:
• Promise.race resolves or rejects based on the first settled promise. If the first promise to settle fails, the returned promise rejects.
• Promise.any resolves based on the first successful promise. It ignores rejections unless all input promises fail, in which case it rejects with an AggregateError.
12. Production Considerations
- • Rate Limiting: Passing massive arrays to
Promise.allcan swamp servers with too many concurrent connections. Limit concurrency using batching utilities or rate limiters (like p-limit) in production.