ReviseAlgo Logo

Asynchronous JavaScript

Promise Combinators

Master JavaScript Promise combinators. Compare Promise.all, Promise.allSettled, Promise.race, and Promise.any in terms of resolve/reject conditions.

Last Updated: July 15, 2026 10 min read

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.all to load dashboard widgets, a single widget load error crashes the entire dashboard. Use Promise.allSettled instead 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.all can swamp servers with too many concurrent connections. Limit concurrency using batching utilities or rate limiters (like p-limit) in production.