Promise.all, Promise.allSettled, Promise.race, and Promise.any look interchangeable in the docs. They're not. Each one has a different failure rule, and the wrong choice in production tends to fail in spectacular ways — usually on a Friday afternoon, usually at scale.

Promise.all Is All Or Nothing

Promise.all rejects on the first failure. The rest of the promises don't get cancelled — they just become invisible. You either get every value, or you get an error.

JavaScript
try {
  const [user, orders, prefs] = await Promise.all([
    fetchUser(id),
    fetchOrders(id),
    fetchPrefs(id),
  ]);
  render(user, orders, prefs);
} catch (err) {
  // any single failure → the whole thing fails
  showError();
}

Use Promise.all when you genuinely cannot proceed without every result — checkout requires user, address, AND payment method; the dashboard renders meaningless without ALL three widgets.

The classic production bug: dev environment is fast and reliable, every promise resolves, the dashboard works. Production has one slow widget that times out, Promise.all rejects, the entire page goes blank. The fix is almost always Promise.allSettled.

allSettled Is For Partial UI

Promise.allSettled always resolves. You get an array of { status, value } or { status, reason } for each promise — letting you render what worked and show errors for what didn't.

JavaScript
const results = await Promise.allSettled([
  fetchUser(id),
  fetchOrders(id),
  fetchPrefs(id),
]);

const get = <T,>(r: PromiseSettledResult<T>): T | null =>
  r.status === 'fulfilled' ? r.value : null;

render({
  user:   get(results[0]),
  orders: get(results[1]),
  prefs:  get(results[2]),
});

This is the right default for dashboards, search results that combine sources, recommendation widgets, and any UI where "show what worked" beats "show nothing." Failures become a UX choice instead of a crash.

race Is For First Result Or Timeout

Promise.race settles with whichever promise settles first — value or error. The classic use is timeouts:

JavaScript
const withTimeout = <T,>(promise: Promise<T>, ms: number): Promise<T> =>
  Promise.race([
    promise,
    new Promise<never>((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), ms)
    ),
  ]);

const user = await withTimeout(fetchUser(id), 5000);

A first-error in the race rejects the whole thing — that's the point. If you want "first success, ignore failures," that's Promise.any, not race.

The other use case for race is querying multiple replicas of the same data: "give me the answer from whichever mirror responds first." When you don't care which source answers, just that you get one.

any Is For First Success

Promise.any resolves with the first promise that succeeds. If all of them reject, you get an AggregateError containing every reason.

JavaScript
try {
  const data = await Promise.any([
    fetchFromCDN1(url),
    fetchFromCDN2(url),
    fetchFromCDN3(url),
  ]);
  use(data);
} catch (err) {
  // all three CDNs failed
  if (err instanceof AggregateError) console.log(err.errors);
  showOfflineMessage();
}

Use Promise.any for fallback patterns: hit several mirrors, take the first one that works. Useful for resilience against partial outages, less useful for everyday business code where you usually want predictable behavior.

Two-by-two decision matrix for choosing a Promise combinator. Axes are fail fast vs tolerant and first result vs all results. Each quadrant labels the right combinator — all, race, allSettled, any — with a one-line example use case.
Two questions. One combinator. Pick by failure mode, not by syntax familiarity.

A Quick Decision Table

Goal Combinator What you get
Need ALL values; one fail = fail Promise.all Array of values, or first error
Want PARTIAL UI; failures are okay Promise.allSettled Array of {status, value/reason}
First to settle (success OR error) Promise.race First settled value, or first error
First SUCCESS only Promise.any First fulfilled value, or AggregateError

Most production bugs in this area are using all where allSettled was needed, or using race where any was needed.

Pro Tips

  1. Default to allSettled when failures shouldn't block the UI.
  2. Use all for atomic operations — checkout, transaction, all-or-nothing flows.
  3. race for timeouts; any for first-success-among-mirrors.
  4. Always handle the rejection. Even allSettled can leave you with all-failed cases the UI must communicate.
  5. Log per-promise failures. Otherwise an allSettled array of errors disappears into the void.

Final Tips

The shortest version: pick the combinator that matches your failure rule. The next time a Promise.all blows up because one slow service was unavailable, you'll wish you'd used allSettled. The next time Promise.race rejects because the first response was an error, you'll wish you'd used any.

Don't read the names. Read the failure semantics.

Good luck — and may your dashboards keep rendering when one widget is sad 👊