All questions

Promise.allSettled

Premium

Promise.allSettled

Implement your own version of Promise.allSettled. You take an iterable of promises (or plain values), kick them all off in parallel, and return a single promise that resolves — never rejects — with an array describing the outcome of each input in input order. Each entry is either { status: 'fulfilled', value } or { status: 'rejected', reason }.

Unlike Promise.all, this one does not short-circuit on rejection. A single bad input does not sink the rest; it simply records its reason and the aggregate waits for every other input to finish.

Signature

// Returns a Promise that always fulfils with an array of outcomes,
// one per input, in input order. Never rejects.
type Outcome<T> =
  | { status: 'fulfilled'; value: T }
  | { status: 'rejected'; reason: unknown };

function promiseAllSettled<T>(
  iterable: Iterable<T | PromiseLike<T>>,
): Promise<Outcome<T>[]>;

Examples

promiseAllSettled([Promise.resolve(1), Promise.reject('boom'), 3])
  .then(console.log);
// [
//   { status: 'fulfilled', value: 1 },
//   { status: 'rejected',  reason: 'boom' },
//   { status: 'fulfilled', value: 3 },
// ]
// Index is preserved — slow input still lands at index 0.
const slow = new Promise((r) => setTimeout(() => r('slow'), 30));
const fast = Promise.resolve('fast');
promiseAllSettled([slow, fast]).then(console.log);
// [
//   { status: 'fulfilled', value: 'slow' },
//   { status: 'fulfilled', value: 'fast' },
// ]
// Every input rejects — the outer promise still RESOLVES.
promiseAllSettled([Promise.reject('a'), Promise.reject('b')])
  .then(console.log)   // runs
  .catch(console.log); // does NOT run
// [
//   { status: 'rejected', reason: 'a' },
//   { status: 'rejected', reason: 'b' },
// ]
// Empty iterable resolves to [] on the next microtask.
promiseAllSettled([]).then(console.log); // []

Notes

  • Always resolves, never rejects. Even if every input rejects, the returned promise fulfils with an array of { status: 'rejected', reason } entries. A .catch on the outer promise should never fire from input rejections.
  • Outcome shape is exact. A fulfilled entry has exactly status and value. A rejected entry has exactly status and reason. No extra keys — tests check Object.keys(...) length.
  • Index, not settle order. results[i] must describe the i-th input. Don't .push() as outcomes arrive.
  • Non-promise inputs pass through as fulfilled. Plain values (42, 'hi', null) and thenables (any object with .then) become fulfilled outcomes via Promise.resolve(item).
  • Iterate the input once. The iterable might be a one-shot generator. A single for...of is the right shape.
  • Sync throws from the iterator don't crash. If the iterable's next() throws partway through, the outer promise still resolves (this version is more forgiving than the spec — it never produces a rejected outer promise). Tests pin this down.
  • Return a real Promise — not the input, not a thenable. Tests check instanceof Promise.
  • Don't use Promise.allSettled or Promise.all to implement this. That defeats the exercise. You may use new Promise(...), Promise.resolve, and .then.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium