Skip to solution
mediumDSA

How do you implement a custom Promise.all polyfill?

724 views
01

Understand the problem

Question presented to candidate: "Promise.all takes an array of promises and resolves once every single one of them resolves, or rejects as soon as any one of them rejects. Can you implement a polyfill for it from scratch? Walk me through the edge cases: what happens with plain, non-promise values mixed into the array, an empty array, and how you keep the results in the right order when the promises do not settle in the order they were given."

What a strong answer should cover:

  • A new outer Promise wraps the whole operation, resolving only once every item has settled, and rejecting immediately on the first rejection.
  • Results must preserve INPUT order, not completion order — each result is written into a pre-sized results array at its own index, never pushed in arrival order.
  • Non-promise values (plain numbers, strings, objects) must be wrapped with Promise.resolve() so they flow through the identical counting logic as real promises.
  • An empty input array is a genuine edge case that needs an explicit check — without one, a naive implementation can silently never resolve at all.
  • A single rejection anywhere calls the outer reject immediately; sibling promises are not cancelled, they simply become irrelevant once the outer promise has already settled.
  • A countdown ("remaining") counter, not an array-length comparison, is what correctly handles the fact that settlement happens asynchronously and out of order.

Clarifying questions expected:

  • "Should the input accept any iterable, or specifically an array?" — determines whether to normalize with Array.from() up front.
  • "Do sibling promises need to be cancelled once the outer promise rejects, or is it fine if they keep running in the background?" — native Promise.all does not cancel them either, so this is worth naming explicitly rather than assuming.

Code / implementation expected: Yes — a full, runnable polyfill plus a real test proving results stay in input order regardless of completion order, and that rejection short-circuits correctly.

promisespolyfillasync
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for JavaScript async/Promise interview questions. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. Every result shown below is real, captured output from actually running the

Solution ready — 2 min read

Classified // press E to declassify

04

Run the code

JSThe polyfill plus a real test proving order preservation, rejection short-circuiting, and native parity (run directly)
Reference: the two real broken variants this doc's pitfalls are based on (run directly to see both bugs)
const delay = (ms, val) => new Promise((r) => setTimeout(() => r(val), ms));

function brokenPushVersion(iterable) {
  return new Promise((resolve, reject) => {
    const items = Array.from(iterable);
    const results = [];
    let remaining = items.length;
    if (remaining === 0) { resolve([]); return; }
    items.forEach((item) => {
      Promise.resolve(item).then((val) => {
        results.push(val); // BUG: completion order, not input order
        remaining -= 1;
        if (remaining === 0) resolve(results);
      }, reject);
    });
  });
}

function brokenNoEmptyCheck(iterable) {
  return new Promise((resolve, reject) => {
    const items = Array.from(iterable);
    const results = new Array(items.length);
    let remaining = items.length;
    items.forEach((item, i) => { // BUG: no check for items.length === 0
      Promise.resolve(item).then((val) => {
        results[i] = val;
        remaining -= 1;
        if (remaining === 0) resolve(results);
      }, reject);
    });
  });
}

(async () => {
  const pushResult = await brokenPushVersion([delay(30, "A-should-be-first"), delay(5, "B-should-be-second")]);
  console.log("push-based result, WRONG order:", JSON.stringify(pushResult));
  // REAL captured output: ["B-should-be-second","A-should-be-first"]

  const race = await Promise.race([
    brokenNoEmptyCheck([]).then(() => "resolved"),
    delay(200, "timeout-fired-instead"),
  ]);
  console.log("empty-array result within 200ms:", race);
  // REAL captured output: "timeout-fired-instead" -- it never resolved
})();
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 67 of 165 decoded in the JavaScript track. One more won't hurt.

Back to track