JavaScript Promise Patterns: all, race, any, and allSettled

Promise combinators let you coordinate multiple asynchronous operations in JavaScript with a single line of code. This article explains how Promise.all, Promise.race, Promise.any, and Promise.allSettled behave, when to use each one, and how to avoid the mistakes that often trip up developers.

Quick answer: Use Promise.all when every task must succeed, Promise.race when you want the first settled result, Promise.any when you want the first successful result, and Promise.allSettled when you need every outcome no matter what.

Difficulty: Intermediate

You'll understand this better if you know: how promises represent async results, how async/await works, and the difference between fulfillment and rejection.

Browser support: Widely available since Jan 2023 (and more below)

1. What Are Promise Patterns?

Promise patterns are built-in methods that combine multiple promises into one result. They solve a common problem: when your code starts several asynchronous tasks at once, you need a predictable way to wait for them, react to the first one that finishes, or inspect every outcome.

  • Promise.all waits for all promises to fulfill.
  • Promise.race settles as soon as the first promise settles, whether that is fulfillment or rejection.
  • Promise.any fulfills as soon as one promise fulfills.
  • Promise.allSettled waits for every promise to finish and reports each result.

These methods are often called promise combinators because they combine multiple async operations into one promise.

2. Why Promise Patterns Matter

Real applications often need to fetch multiple resources, run several checks in parallel, or provide a fallback when one service is slow. Promise patterns help you write that logic clearly instead of manually counting completions and tracking state.

They also make your code easier to reason about. Instead of nesting callbacks or writing custom bookkeeping, you can express your intent directly: wait for everything, use the fastest response, accept the first success, or collect all results.

3. Basic Syntax

All four methods accept an iterable, usually an array of promises. The return value is always a promise, but each method resolves according to a different rule.

Promise.all syntax

Use Promise.all when all results matter and a single failure should fail the whole operation.

JavaScript
const results = await Promise.all([promiseA, promiseB, promiseC]);

If every promise fulfills, the returned array preserves the original order of the input, not the completion order.

Promise.race syntax

Use Promise.race when the first settled outcome should win.

JavaScript
const winner = await Promise.race([promiseA, promiseB]);

The returned promise settles with the same value or reason as the first promise to settle.

Promise.any syntax

Use Promise.any when you want the first successful result and can ignore earlier failures.

JavaScript
const value = await Promise.any([promiseA, promiseB]);

If every promise rejects, the returned promise rejects with an AggregateError.

Promise.allSettled syntax

Use Promise.allSettled when you need the result of every promise, even if some fail.

JavaScript
const outcomes = await Promise.allSettled([promiseA, promiseB]);

The result is an array of objects describing whether each promise was fulfilled or rejected.

4. Step-by-Step Examples

Example 1: Waiting for every request with Promise.all

Suppose you need a profile, notifications, and settings before rendering a dashboard. You can start the requests together and wait until all of them finish.

JavaScript
const profilePromise = fetch("/api/profile").then(response => response.json());
const notificationsPromise = fetch("/api/notifications").then(response => response.json());
const settingsPromise = fetch("/api/settings").then(response => response.json());

const [profile, notifications, settings] = await Promise.all([
  profilePromise,
  notificationsPromise,
  settingsPromise
]);

This pattern is useful when every value is required. If one request fails, the whole operation rejects immediately.

Example 2: Using the fastest response with Promise.race

Imagine a request that may be served from a cache or from the network. You can race them and take the first one to settle.

JavaScript
const cachePromise = readFromCache("user:42");
const networkPromise = fetch("/api/users/42").then(response => response.json());

const result = await Promise.race([
  cachePromise,
  networkPromise
]);

The first settled promise wins, even if that outcome is a rejection. That makes Promise.race useful for timeouts and fallback strategies, but it also means you must be careful about failures.

Example 3: Accepting the first success with Promise.any

If you have multiple mirrors for the same resource, you may want the first successful response and ignore early failures.

JavaScript
const mirrorA = fetch("https://mirror-a.example.com/data.json").then(response => response.json());
const mirrorB = fetch("https://mirror-b.example.com/data.json").then(response => response.json());
const mirrorC = fetch("https://mirror-c.example.com/data.json").then(response => response.json());

const data = await Promise.any([mirrorA, mirrorB, mirrorC]);

This is different from Promise.race. A rejected promise does not win unless every promise rejects.

Example 4: Collecting all outcomes with Promise.allSettled

When you need to report which tasks succeeded and which failed, Promise.allSettled is the safest choice.

JavaScript
const tasks = [
  fetch("/api/a"),
  fetch("/api/b"),
  fetch("/api/c")
];

const results = await Promise.allSettled(tasks);

for (const result of results) {
  if (result.status === "fulfilled") {
    console.log(result.value);
  } else {
    console.error(result.reason);
  }
}

The result array gives you one object per input promise, making it easy to display partial success.

5. Practical Use Cases

  • Load several API resources in parallel before rendering a view.
  • Use a timeout by racing a request against a timer promise.
  • Query redundant services and accept the first successful response.
  • Run batch jobs where every result must be reported, even failures.
  • Process multiple uploads and show a per-file success or failure summary.

These patterns appear in dashboards, search, file processing, monitoring tools, and reliability-sensitive applications.

6. Common Mistakes

Mistake 1: Expecting Promise.all to keep successful results after one failure

Promise.all rejects as soon as any input promise rejects. Many beginners expect it to return the successful values and ignore the failure, but that is what Promise.allSettled is for.

Problem: If one request fails, the whole Promise.all call rejects and you do not get a partial array of fulfilled values.

JavaScript
const profile = fetch("/api/profile").then(response => response.json());
const settings = fetch("/api/settings").then(response => response.json());
const badRequest = Promise.reject(new Error("settings unavailable"));

const data = await Promise.all([profile, settings, badRequest]);

Fix: Use Promise.allSettled when you want every outcome, or catch errors before combining them if a fallback is acceptable.

JavaScript
const results = await Promise.allSettled([profile, settings, badRequest]);

const successfulValues = results
  .filter(result => result.status === "fulfilled")
  .map(result => result.value);

The corrected version works because it is designed for mixed success and failure.

Mistake 2: Using Promise.race when you really want the first success

Promise.race does not ignore rejections. If the fastest promise rejects, the entire race rejects immediately.

Problem: A slow successful request cannot save the result if a faster request fails first.

JavaScript
const fastFail = Promise.reject(new Error("cache miss"));
const slowSuccess = new Promise(resolve => setTimeout(() => resolve("network data"), 100));

const value = await Promise.race([fastFail, slowSuccess]);

Fix: Use Promise.any when you need the first fulfilled result, not the first settled result.

JavaScript
const value = await Promise.any([fastFail, slowSuccess]);

The corrected version waits for the first success instead of failing on the first rejection.

Mistake 3: Ignoring Promise.any rejection behavior

Some developers assume Promise.any never rejects because it can skip failures. In reality, it rejects when all input promises reject, and the error is an AggregateError.

Problem: If every promise fails, the catch block receives an aggregate of all reasons, not a normal single error message.

JavaScript
try {
  await Promise.any([
    Promise.reject(new Error("A failed")),
    Promise.reject(new Error("B failed"))
  ]);
} catch (error) {
  console.log(error.message);
}
Output✓ Tested · Node 24
All promises were rejected

Fix: Check for AggregateError and inspect its errors array when you need the reasons for failure.

JavaScript
try {
  await Promise.any([
    Promise.reject(new Error("A failed")),
    Promise.reject(new Error("B failed"))
  ]);
} catch (error) {
  if (error instanceof AggregateError) {
    for (const reason of error.errors) {
      console.error(reason.message);
    }
  }
}
Output✓ Tested · Node 24
A failed
B failed

The corrected version handles the real shape of the failure.

7. Best Practices

Practice 1: Start independent promises before awaiting

To get real concurrency, create the promises first and await the combined result afterward. If you await each request one by one, you turn parallel work into serial work.

JavaScript
const userPromise = fetch("/api/user");
const postsPromise = fetch("/api/posts");

const [userResponse, postsResponse] = await Promise.all([userPromise, postsPromise]);

This pattern reduces total wait time because both requests run at the same time.

Practice 2: Choose the combinator that matches your failure policy

Your error strategy should drive the method you choose. If the task fails when any part fails, use Promise.all. If partial results are acceptable, use Promise.allSettled. If one success is enough, use Promise.any.

JavaScript
const chosen = await Promise.allSettled([taskA, taskB, taskC]);

Matching the method to the business rule makes code easier to maintain and less surprising.

Practice 3: Handle rejected outcomes explicitly

With Promise.allSettled, do not assume every result has a value. Read the status field before touching value or reason.

JavaScript
const results = await Promise.allSettled([taskA, taskB]);

for (const result of results) {
  if (result.status === "fulfilled") {
    console.log(result.value);
  }
}
Output✓ Tested · Node 24
Uncaught ReferenceError: taskA is not defined

This avoids undefined access and keeps your success and failure paths clear.

8. Limitations and Edge Cases

  • Promise.race does not mean “fastest success”; it means “first settled promise,” which can be a rejection.
  • Promise.any requires at least one fulfillment and throws AggregateError if all promises reject.
  • Promise.all keeps the order of inputs, not the order in which promises finish.
  • Passing plain values instead of promises usually works because these methods wrap them with Promise.resolve, but that can hide mistakes in async logic.
  • None of these methods cancel the promises that lose the race or are no longer needed; the underlying work may continue unless you add cancellation yourself.

That last point matters in browser and server code alike. If you are racing fetch requests, the slower ones may still consume bandwidth unless you explicitly abort them with a separate cancellation mechanism.

9. Practical Mini Project

Here is a small dashboard helper that loads three widgets in parallel, accepts the first successful fallback source for featured content, and records which widgets failed without breaking the page.

JavaScript
async function loadDashboard() {
  const statsPromise = fetch("/api/stats").then(response => response.json());
  const alertsPromise = fetch("/api/alerts").then(response => response.json());
  const featuredA = fetch("/api/featured-a").then(response => response.json());
  const featuredB = fetch("/api/featured-b").then(response => response.json());

  const [statsResult, alertsResult] = await Promise.allSettled([
    statsPromise,
    alertsPromise
  ]);

  let featured;
  try {
    featured = await Promise.any([featuredA, featuredB]);
  } catch {
    featured = null;
  }

  return {
    stats: statsResult.status === "fulfilled" ? statsResult.value : null,
    alerts: alertsResult.status === "fulfilled" ? alertsResult.value : null,
    featured
  };
}

This example shows a realistic combination of promise patterns: allSettled for partial data, any for fallback content, and normal promise chaining for JSON parsing.

10. Key Points

  • Promise.all is for “all must succeed.”
  • Promise.race is for “first settled wins.”
  • Promise.any is for “first fulfilled wins.”
  • Promise.allSettled is for “show me every outcome.”
  • The returned results preserve input order, even when completion order differs.
  • These methods do not cancel slower promises automatically.

11. Practice Exercise

  • Create three promises that resolve after different delays.
  • Use Promise.all to wait for them and print the results in input order.
  • Then change one promise to reject and compare the behavior of Promise.all and Promise.allSettled.

Expected output: The Promise.all version should log an array only when all promises fulfill. The Promise.allSettled version should log one result object per promise, including the rejection.

Hint: Use setTimeout inside new Promise to control the timing.

Show solution
JavaScript
const makePromise = (label, delay, shouldReject = false) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldReject) {
        reject(new Error(`${label} failed`));
      } else {
        resolve(label);
      }
    }, delay);
  });
};

const a = makePromise("A", 300);
const b = makePromise("B", 100, true);
const c = makePromise("C", 200);

try {
  const allResults = await Promise.all([a, b, c]);
  console.log(allResults);
} catch (error) {
  console.error(error.message);
}

const settledResults = await Promise.allSettled([a, b, c]);
console.log(settledResults);
Output✓ Tested · Node 24
B failed
[ { status: "fulfilled", value: "A" }, { status: "rejected", reason: Error: B failed }, { status: "fulfilled", value: "C" } ]

Browser Support

FeatureChromeEdgeFirefoxSafariSafari iOS
Promise.allSettled()
Widely available since Jan 2023
76+79+71+13+13+
Promise.any()
Widely available since Mar 2023
85+85+79+14+14+
AbortSignal.any()
Widely available since Sep 2026
116+116+124+17.4+17.4+

Minimum versions with full support. Data from web-features 3.43.0 (the data behind MDN's Baseline), updated at every site build.

12. Final Summary

Promise patterns give you a compact, reliable way to coordinate multiple asynchronous operations in JavaScript. The key is to match the method to the behavior you want: wait for everything with Promise.all, use the first settled value with Promise.race, accept the first success with Promise.any, or inspect every outcome with Promise.allSettled.

Once you understand the difference between settlement and fulfillment, these methods become easy to choose correctly. They are especially useful for API requests, fallback systems, batch processing, and partial-failure reporting.

Next, practice combining these methods with async/await and timed requests so you can recognize which pattern fits a given problem quickly.