JavaScriptPromise.all, race, any, allSettled

Promise Combinators

The promise combinators are four static methods on Promise that take a collection of promises and produce a single promise describing how they finish together: Promise.all, Promise.allSettled, Promise.race and Promise.any. Each answers a different question — all must succeed, tell me how each one ended, the first to finish, the first to succeed — and picking the right one usually replaces a pile of bookkeeping.

Promise.all — succeed together or fail fast

Promise.all(iterable) waits for every promise to fulfil and resolves with an array of their values in input order. If any promise rejects, the returned promise rejects immediately with that reason — the others keep running but their results are ignored.

JS
const users = await Promise.all([
  fetch("/api/user/1").then((r) => r.json()),
  fetch("/api/user/2").then((r) => r.json()),
  fetch("/api/user/3").then((r) => r.json()),
]);

console.log(users.length);   // 3, in the same order as the input

Use Promise.all when every result is required and a single failure should abort the batch — for example, loading three files that together form one page.

Fail-fast can leak work
Other requests do not get cancelled when one rejects. If cancellation matters, pair `Promise.all` with an `AbortController` whose signal you share between the requests.
Promise.allSettled — wait for all outcomes

Promise.allSettled also waits for every promise, but it never rejects. The result is an array of result descriptors — one per input — that tell you, for each, whether it fulfilled and with what, or rejected and with what reason.

JS
const results = await Promise.allSettled([
  fetch("/api/a"),
  fetch("/api/b"),
  fetch("/api/c"),
]);

for (const r of results) {
  if (r.status === "fulfilled") console.log("ok", r.value.status);
  else console.log("failed", r.reason.message);
}

Reach for allSettled when each task is independent and you want to report partial success — bulk imports, dashboards that show "3 of 5 sources loaded", retry batches.

Promise.race — first to settle wins

Promise.race resolves or rejects as soon as any input settles, copying its value or reason. The other promises continue running but their outcomes are discarded.

JS
function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error("timeout")), ms),
  );
  return Promise.race([promise, timeout]);
}

try {
  const data = await withTimeout(fetch("/slow"), 2000);
  console.log("got data");
} catch (err) {
  console.log(err.message);   // "timeout" if the fetch is too slow
}

Timeouts are the canonical use. Less obvious uses: racing the same query against multiple replicas, or against a cached value and a live fetch.

Promise.any — first success wins

Promise.any is the opposite of Promise.all: it resolves with the value of the first promise that fulfils and only rejects if every input rejects. The combined rejection is an AggregateError whose .errors array lists each failure.

JS
try {
  const fastest = await Promise.any([
    fetch("https://mirror-1.example.com/file"),
    fetch("https://mirror-2.example.com/file"),
    fetch("https://mirror-3.example.com/file"),
  ]);
  console.log("served from", fastest.url);
} catch (err) {
  console.log(err.errors);   // every mirror failed
}

Use any when you have several equivalent sources and only need one to succeed — mirrors, CDN fallbacks, multi-region reads.

Side-by-side comparison
  • Promise.allall fulfil, fail fast. Result: array of values in input order.

  • Promise.allSettledall settle, never rejects. Result: array of {status, value | reason} descriptors.

  • Promise.racefirst to settle (fulfil or reject). Result or rejection: that one.

  • Promise.anyfirst to fulfil. Rejection: AggregateError only if every input rejected.

Empty inputs
Edge cases worth remembering: `Promise.all([])` fulfils immediately with `[]`, `Promise.allSettled([])` fulfils with `[]`, `Promise.race([])` stays **pending forever**, and `Promise.any([])` rejects with an empty `AggregateError`.
Mixing values and promises

All four combinators accept any iterable — and any non-promise value is treated as an already-resolved promise. This lets you mix sync defaults with async lookups without special-casing.

JS
const [config, user] = await Promise.all([
  defaultConfig,                       // a plain object
  fetch("/me").then((r) => r.json()),  // a promise
]);
Concurrency limits — what combinators do not give you

Promise.all starts every task at once. With hundreds of URLs you usually want a concurrency limit. A tiny helper:

JS
async function mapLimit(items, limit, worker) {
  const results = new Array(items.length);
  let next = 0;
  async function run() {
    while (next < items.length) {
      const i = next++;
      results[i] = await worker(items[i], i);
    }
  }
  await Promise.all(Array.from({ length: limit }, run));
  return results;
}

const pages = await mapLimit(urls, 5, (url) => fetch(url).then((r) => r.text()));
Pick by question, not by familiarity
Ask "what do I want to do if some of them fail?" — *abort* (all), *report* (allSettled), *take the first* (race), *take the first success* (any). The right combinator falls out of the answer.