skip to content

What is the difference between Promise.all and Promise.allSettled in JavaScript, and what does each of them fulfil with?

level: juniorimportance: must knowfreq 78%

answer

  1. one aggregate promise, two failure policies
  2. fail-fast versus wait-for-everyone
  3. status descriptors instead of bare values
  4. first rejection reason, other results discarded
  5. ES2020 addition that never rejects

basics

~20 s

Promise.all rejects the moment any input promise rejects, and otherwise fulfils with an array of the values in input order. Promise.allSettled waits for every input and always fulfils, giving one {status, value or reason} object per entry.

solid answer

~40 s

Both take an iterable of promises and return a single aggregate promise, but they disagree on what counts as done. `Promise.all` is all-or-nothing: it fulfils with an array of values, positionally matched to the input, only if every input fulfils; if any input rejects it rejects immediately with that first rejection reason and discards the other results. `Promise.allSettled`, added in ES2020, never rejects — it waits for every input to settle either way and fulfils with an array of descriptor objects, `{ status: 'fulfilled', value }` for successes and `{ status: 'rejected', reason }` for failures. So I reach for `all` when the operation is meaningless without every piece and I want the error to propagate, and for `allSettled` when partial results are useful and I need to see which entries failed and why.

go deeper

for a junior

Be able to state the two behaviours plainly: one rejects on the first failure and gives you an array of values, the other always succeeds and gives you a status object per entry. Naming the {status, value, reason} shape from memory is expected.

for a middle

Explain the mechanics behind the difference — positional result ordering, fail-fast rejecting with a single reason, and the fact that on the happy path both settle at the same time. Be ready to write the filter-by-status code that splits successes from failures.

for a senior

Show judgment about which failure policy the caller actually needs, and point out that a fail-fast rejection leaves the sibling work running with its side effects intact. Interviewers expect you to raise the diagnostic loss when only the first reason survives.

for a principal

Own the API-contract angle: an all-or-nothing endpoint and a partial-results endpoint are different contracts, and switching to allSettled means the response must carry per-section status plus logging for the swallowed reasons. Decide it from the invariants the response must uphold, not from taste.

## The shared shape `Promise.all` and `Promise.allSettled` are both static methods on the `Promise` constructor. Each takes an **iterable** — normally an array, but any iterable works — walks it, passes every entry through `Promise.resolve` so plain values and thenables are treated uniformly, and returns one new promise that aggregates the whole batch. Neither of them *starts* anything: by the time you build the array, the functions inside it have already been called and the underlying work is already in flight. The combinator is only a way to observe when the batch is done. They differ in exactly one decision: what counts as "done", and therefore what the aggregate promise settles with. ## Promise.all — all-or-nothing `Promise.all` fulfils only when **every** input has fulfilled. Its fulfilment value is an array of the individual values, in the **order of the input iterable**, regardless of which finished first. ```js const [user, orders] = await Promise.all([fetchUser(id), fetchOrders(id)]); ``` If any input rejects, the aggregate rejects **immediately** with that single rejection reason — not an array of reasons, not a partial result. This is called fail-fast. The `await` above throws exactly as if a single call had thrown, which is why `Promise.all` composes so naturally with `try/catch`: the failure of any part is the failure of the whole. Two consequences follow that candidates often miss. First, the values of the inputs that did succeed are gone — the aggregate promise settles once, and it settled as a rejection. Second, rejecting the aggregate does not stop the other inputs: a promise is a handle on work that is already running, not a job you can call off, so the remaining requests still complete and their side effects still land. ## Promise.allSettled — a report per entry `Promise.allSettled` (ES2020) fulfils once every input has **settled**, fulfilled or rejected. It essentially never rejects; you always get an array back, again positionally matched to the input. Each element is a descriptor object: ```js const results = await Promise.allSettled([a(), b(), c()]); // [{ status: 'fulfilled', value: 1 }, // { status: 'rejected', reason: Error }, // { status: 'fulfilled', value: 3 }] ``` A fulfilled descriptor has `status` and `value`; a rejected one has `status` and `reason`. The other property is simply absent, so reading `.value` on a rejected entry gives `undefined` rather than throwing. Because the aggregate always fulfils, **you** are responsible for inspecting statuses — nothing throws on your behalf: ```js const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value); const failures = results.filter(r => r.status === 'rejected').map(r => r.reason); ``` ## Timing: which one settles sooner A common assumption is that `allSettled` is "the slow one". On the happy path both settle at the same moment — when the slowest input finishes. They diverge only when something rejects: `all` can settle much earlier (the instant of the first rejection), while `allSettled` still waits for every straggler. If you need every operation to be finished before you continue — cleanup, a summary log, a transaction boundary — `allSettled` is the one that actually guarantees it. ## Choosing between them Ask whether the caller's result is meaningful without one of the pieces. A page that needs the user record *and* their permissions is all-or-nothing: use `all` and let the rejection propagate. A dashboard with five independent widgets, or a batch importer processing 200 rows, is not: use `allSettled`, render what arrived, and report the rest. ## Edge cases worth knowing - Both fulfil immediately with `[]` for an empty iterable. - Non-promise entries pass straight through: `Promise.all([1, Promise.resolve(2)])` fulfils with `[1, 2]`. - `Promise.all` rejects with the *first* reason only; later rejections are dropped. They do not surface as unhandled rejections, because `all` attached handlers to every input up front — but the diagnostic detail in them is lost unless you capture it yourself. - Neither offers cancellation, and neither retries; those are separate concerns you layer on top.

  • If two of the inputs to Promise.all reject, what happens to the second rejection?
    The aggregate promise has already settled with the first reason, so the second is simply ignored — a promise settles once. It does not become an unhandled rejection either, because `Promise.all` attached a handler to every input when it was called. The practical cost is diagnostic: you never see that second error unless you wrap each input in its own `.catch` or use `Promise.allSettled` instead.
  • Does Promise.allSettled ever reject?
    For practical purposes no — every input settling either way still fulfils the aggregate. The only realistic way to get a rejection is a malformed argument: passing a non-iterable, or an iterator whose `next` throws, rejects the returned promise with that error. That is a programmer mistake, not a task failure, so you still cannot rely on `try/catch` to reveal that one of your operations failed.
  • What does Promise.all fulfil with if you pass it an empty array?
    It fulfils right away with an empty array. `Promise.allSettled([])` behaves the same way. This matters when the batch size is computed at runtime: an empty result is not a signal that something went wrong, so code that treats `results.length === 0` as an error will misfire on the legitimate no-work case.

saying these in an interview costs you the question

  • Claims Promise.all rejects with an array of all errors
  • Says Promise.allSettled rejects when any entry fails
  • Thinks Promise.all returns results in completion order
  • Believes a rejected Promise.all cancels the remaining work
  • Assumes allSettled entries are the plain resolved values

context