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.
// 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>[]>;
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); // []
{ status: 'rejected', reason } entries. A .catch on the outer promise should never fire from input rejections.status and value. A rejected entry has exactly status and reason. No extra keys — tests check Object.keys(...) length.results[i] must describe the i-th input. Don't .push() as outcomes arrive.42, 'hi', null) and thenables (any object with .then) become fulfilled outcomes via Promise.resolve(item).for...of is the right shape.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.instanceof Promise.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.