Skip to the solution
HardDSA10 min read

How do you implement a simple Promise library from scratch?

179 views

The problem

Question presented to candidate: "Implement a simplified version of the Promise class from scratch β€” the constructor, then, catch, and finally. It needs to correctly handle chaining, a handler that throws, a handler that returns another promise or thenable, and calling resolve more than once. How do you make sure then callbacks run asynchronously, the way real Promise callbacks do, instead of synchronously?"

What a strong answer should cover:

  • A Promise has exactly three states β€” pending, fulfilled, rejected β€” and once it leaves pending it can never change state again; calling resolve or reject a second time is a genuine no-op.
  • .then() must return a NEW promise, not the same one, which is what makes chaining (.then().then().then()) work at all β€” each .then() call's return value becomes the next link's input.
  • If a .then() handler throws, the returned promise must reject with that thrown value, and that rejection should propagate to the next .catch(), skipping over any .then() calls without a rejection handler in between.
  • If resolve is called with a thenable (any object with a callable .then, not just another instance of this exact class), the outer promise must wait for and adopt that thenable's eventual state, recursively, not just fulfill immediately with the thenable object itself.
  • Callbacks registered with .then() must run asynchronously even if the promise is already settled by the time .then() is called β€” real Promises always defer to a microtask, never call a handler synchronously inline.
  • .finally() runs its callback on both success and failure, and does not change the eventual value or error β€” it passes both straight through.

Clarifying questions expected:

  • "Should pending callbacks be stored per-promise and flushed once it settles, or is there a simpler model you would prefer to reach for first?" β€” tests whether the candidate understands why a callback queue is structurally necessary.
  • "Is queueMicrotask available in this environment, or should I simulate microtask timing with something like Promise.resolve().then() or setTimeout as a fallback?" β€” a real, practical environment question, since queueMicrotask itself is a relatively recent global.

Code / implementation expected: Yes β€” a full, runnable implementation plus a real test suite proving chaining, error propagation, thenable adoption, and β€” critically β€” that its actual microtask ordering matches native Promise exactly.

  • promises
  • polyfill
  • async

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

10 min read Β· 7 sections

Run the code

Edit the code and run it here. Each console line with an expected value becomes a test.

JavaScriptMyPromise, a from-scratch Promise implementation, plus its real 10-group test suite (run directly)

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 161 of 165 in JavaScript

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextHard

How would you use Symbol.hasInstance and Symbol.toPrimitive to customize instanceof checks and type coercion for a class?

Continue