Promise.race and Promise.any Semantics
Promise.race settles as soon as the first input promise settles, whether fulfilled or rejected. Promise.any settles successfully on the first fulfillment and rejects only after every input rejects.
Both `Promise.race()` and `Promise.any()` accept an iterable of promises or other values and return a new promise. The key difference is what event determines the result of that returned promise.
`Promise.race()` mirrors the first input to settle. If the first settled input fulfills, the returned promise fulfills with that value; if it rejects first, the returned promise rejects with that reason. Later inputs do not change the already-settled result, although they continue running.
`Promise.any()` is fulfillment-oriented. It resolves as soon as any input fulfills, ignoring earlier rejections. If every input rejects, it rejects with an `AggregateError` containing the rejection reasons.
Both methods observe all supplied inputs even though the returned promise may settle before some inputs finish. Neither method cancels the remaining asynchronous operations automatically, so cancellation requires a separate mechanism such as `AbortController` when the underlying API supports it.
A useful interview distinction is that `race` asks "which operation settles first?", while `any` asks "which operation succeeds first?".
Key points
- `Promise.race()` settles on the first fulfillment or rejection.
- `Promise.any()` fulfills on the first fulfillment.
- `Promise.any()` rejects only when every input rejects.
- An all-rejected `Promise.any()` produces an `AggregateError`.
- Neither method automatically cancels promises that lose the race.
Interview questions
What is the difference between Promise.race() and Promise.any()?
`Promise.race()` settles according to whichever input settles first, regardless of whether that input fulfills or rejects. `Promise.any()` settles successfully when the first input fulfills. Rejections are ignored by `any` until all inputs have rejected, at which point it rejects with an `AggregateError`.
What happens if the fastest promise rejects when using Promise.race()?
The returned promise rejects with that rejection reason. This happens because `race` responds to the first settlement rather than waiting for a successful result. Later fulfillments cannot change the state of the already-rejected returned promise.
What does Promise.any() do when all inputs reject?
The returned promise rejects after every input has rejected. Its rejection value is an `AggregateError`, which represents the collection of failures. The error exposes the individual rejection reasons through its `errors` property.
Do Promise.race() and Promise.any() cancel the promises that do not determine the result?
No. These combinators settle their returned promise, but they do not automatically stop the underlying asynchronous operations. For network requests or other cancellable work, cancellation must be implemented separately. In browser APIs that support it, `AbortController` is a common approach.
When would you choose Promise.any() instead of Promise.race() in a frontend application?
I would use `Promise.any()` when several alternatives can provide an acceptable successful result and failures from individual alternatives should not immediately fail the operation. For example, multiple equivalent endpoints could be queried and the first successful response accepted. I would use `Promise.race()` when the earliest completion itself is what matters, including cases where an early rejection should determine the result. In either case, I would consider whether the remaining operations need explicit cancellation.