23. Implement Promise.allSettled — Frontend Interview Question

medium

If you've written Promise.all, this is the same skeleton with one rule flipped: allSettled never rejects. It waits for everything to finish and tells you how each one went. That single difference is the whole question.

All vs allSettled

Promise.all rejects the moment any input rejects, and you lose the results of the ones that succeeded. Promise.allSettled waits for all of them and gives you an array of result objects: { status: "fulfilled", value } or { status: "rejected", reason }. Use it when you want every outcome regardless of failures, like firing six independent API calls and rendering whatever came back.

The implementation

function allSettled(promises) {
  return new Promise((resolve) => {
    const results = [];
    let settled = 0;

    if (promises.length === 0) return resolve(results);

    promises.forEach((p, i) => {
      // Promise.resolve wraps non-promise values so .then is safe.
      Promise.resolve(p).then(
        (value) => {
          results[i] = { status: "fulfilled", value };
          if (++settled === promises.length) resolve(results);
        },
        (reason) => {
          results[i] = { status: "rejected", reason };
          if (++settled === promises.length) resolve(results);
        }
      );
    });
  });
}

Three details worth narrating

  1. Write by index, not push. results[i] = ... keeps the output aligned with the input order even though promises finish at different times. Pushing would scramble them.
  2. The counter, not the array length, decides when you're done. A slot can be undefined mid-flight, so checking results.length lies. Count completions.
  3. It only ever resolves. Notice there's no reject anywhere. A rejected input is recorded as data, not propagated as a failure. If your version can reject, you've written Promise.all by accident.

The edge case interviewers slip in

An empty array. The real allSettled([]) resolves immediately with []. Without the early resolve(results), your counter never reaches zero and the promise hangs forever. Cheap to handle, easy to forget.

What to practice

In the editor, get the mixed fulfilled/rejected case passing, then confirm order is preserved when a slow promise sits before a fast one. If both are green, try the empty-array case yourself.

More questions

index.js
function allSettled(promises) {
  // your code here
}

// Try it:
allSettled([
  Promise.resolve(1),
  Promise.reject("nope"),
  Promise.resolve(3),
]).then((results) => console.log(results));
// [
//   { status: "fulfilled", value: 1 },
//   { status: "rejected", reason: "nope" },
//   { status: "fulfilled", value: 3 },
// ]

Tests

Test Code

Enter JavaScript that runs after your solution. It should return a value or a Promise.