Promise combinators coordinate multiple async operations. all fails fast, allSettled always returns results, race resolves/rejects with the first settled promise, and any resolves with the first fulfilled promise (or rejects with AggregateError if all reject). Production answers map each one to partial-failure UIs, timeout guards, and fallback endpoints.
Use this JavaScript interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
Promise.all vs allSettled vs race vs any (JavaScript Promise Combinators)Frontend interview answer
This JavaScript interview question tests whether you can explain Promise combinators: all vs allSettled vs race vs any in production, connect it to production trade-offs, and handle common follow-up questions.
- Promise combinators: all vs allSettled vs race vs any in production explanation without falling back to memorized docs wording
- Promise and Async reasoning, edge cases, and production failure modes
- How you would answer the most likely JavaScript interview follow-up
The mental model
All four take an iterable of promises (values are auto-wrapped via Promise.resolve). The key differences are:
1) What “finishes” the combinator (first failure? first settle? first success? all done?)
2) What it returns (values only vs per-promise status objects)
3) How errors behave (fail-fast vs aggregate)
Interviewers usually want one real use case per combinator: all for dependencies, allSettled for partial-failure UIs, race for timeout guards, and any for fallback mirrors. Also: these combinators do not cancel the other promises; they just decide what result to produce.
Combinator | Resolves when... | Rejects when... | Resolved value shape | Typical use |
|---|---|---|---|---|
Promise.all | All promises fulfill | Any promise rejects (fail-fast) | Array of fulfillment values (in input order) | All-or-nothing dependencies (e.g., load config + user + flags) |
Promise.allSettled | All promises settle (fulfilled or rejected) | Never (unless input iteration throws) | Array of result objects: | Collect outcomes (e.g., show partial results, log failures) |
Promise.race | First promise settles and it fulfills | First promise settles and it rejects | The first settled value (or rejection reason) | Timeouts / “first response wins” |
Promise.any | First promise fulfills | All promises reject | The first fulfilled value; rejects with | Fallbacks (e.g., first successful endpoint/CDN) |
Minimal examples
const ok1 = Promise.resolve('A');
const ok2 = Promise.resolve('B');
const bad = Promise.reject(new Error('X'));
// 1) all: fails fast on first rejection
await Promise.all([ok1, ok2]); // -> ['A', 'B']
await Promise.all([ok1, bad, ok2]); // -> rejects with Error('X')
// 2) allSettled: always returns status objects
await Promise.allSettled([ok1, bad, ok2]);
// -> [
// { status: 'fulfilled', value: 'A' },
// { status: 'rejected', reason: Error('X') },
// { status: 'fulfilled', value: 'B' }
// ]
// 3) race: first settle wins (could be reject)
await Promise.race([bad, ok1]); // -> rejects (bad settles first)
await Promise.race([ok1, bad]); // -> 'A'
// 4) any: first fulfillment wins (ignores rejects until all reject)
await Promise.any([bad, ok2]); // -> 'B'
await Promise.any([bad, Promise.reject('Y')]);
// -> rejects with AggregateError (contains all reasons)
Important details interviewers look for
1) Input order: all and allSettled keep the output array aligned with the input order, not completion order.
2) Fail-fast vs keep-going:
allrejects as soon as one rejects.
allSettledwaits for all.
racesettles on the first settled promise (resolve or reject).
anyresolves on the first fulfillment; only rejects if everything rejects.
3) No cancellation: Promise.all rejecting does not stop the other operations. If you need cancellation, you need explicit support (e.g., AbortController for fetch).
4) AggregateError: Promise.any rejects with an AggregateError, which bundles all rejection reasons (often via error.errors in many runtimes).
Classic pattern: timeout with race
Use race to bound time. But remember: the original async work continues unless it’s cancellable.
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
}
async function fetchWithTimeout(url, ms, signal) {
// Prefer real cancellation (AbortController) when possible.
const fetchPromise = fetch(url, { signal });
return Promise.race([fetchPromise, timeout(ms)]);
}
Classic pattern: first successful response with any
async function firstHealthy(urls) {
return Promise.any(
urls.map(async (u) => {
const r = await fetch(u);
if (!r.ok) throw new Error(`${u} -> ${r.status}`);
return r;
})
);
}
// If all endpoints fail -> AggregateError
try {
await Promise.any([mirrorA(), mirrorB(), mirrorC()]);
} catch (error) {
if (error instanceof AggregateError) {
console.error(error.errors); // every rejection reason
}
}
Decision shortcut
Promise.allfor “I need every dependency before render”.Promise.allSettledfor dashboards or batch jobs where partial failure is still useful.Promise.racefor timeout guards or first-settled control flow.Promise.anyfor mirrors, fallbacks, or “first healthy response wins”.
One-liners to memorize
- all: “All succeed or I fail fast.”
- allSettled: “Tell me how each one ended.”
- race: “First one to settle decides.”
- any: “First success wins; otherwise aggregate failure.”
Use this as one explanation rep, then continue with the JavaScript interview questions cluster or a guided prep path.