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.
Frontend interview answer
Promise.all vs allSettled vs race vs any (JavaScript Promise Combinators)
Interview quick answer
Interview focus
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 definitions
- Promise and Async reasoning, edge cases, and production failure modes
- How you would answer the most likely JavaScript interview follow-up
Use this JavaScript interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
Full interview answer
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.