Implement a Promise from Scratch

Build a fully spec-compliant Promise class step by step: the state machine, then-chaining with microtask scheduling, the resolution procedure for thenable adoption, and static helpers.

16 min read
JavaScript
Interview
Implementation
Promises

TABLE OF CONTENTS

Building a Promise from scratch is the deepest async interview question. You need to implement the state machine, async handler queuing, .then() chaining, and the resolution procedure — the rule that lets a .then() handler return another Promise and have it "unwrap" automatically.


What is a Promise?

A Promise is a state machine with three states: pending, fulfilled, and rejected. Once it transitions from pending, the state is locked — it can never change again.

The hard part isn't the state machine. It's two things:

  1. .then() must be async — even if the promise is already settled, handlers must run asynchronously (in the microtask queue, not synchronously). This matches the Promises/A+ spec and is what makes promise behavior predictable.
  2. The resolution procedure — when a .then() handler returns a thenable (another Promise, or any object with a .then method), the new promise must "adopt" that thenable's state rather than resolving with the thenable itself. This is what makes chaining work.

The Problem

"Implement a MyPromise class that behaves like the native Promise. It should support resolve, reject, .then() chaining, .catch(), and .finally()."


Thought Process

Build it layer by layer:

  1. State machine — constructor, #resolve, #reject
  2. Handler queue — store .then() callbacks for when the promise settles
  3. Async execution — use queueMicrotask so handlers always run after current synchronous code
  4. .then() returns a new Promise — the result of the handler becomes the new promise's value
  5. Resolution procedure — if a handler returns a thenable, adopt its state
  6. .catch() and .finally() — thin wrappers around .then()

Step 1 — State Machine

Loading editor...


Step 2 — .then() with Chaining

.then() must return a new Promise. The new promise resolves with whatever the handler returns — or rejects if the handler throws.

Loading editor...


Step 3 — The Resolution Procedure (Thenable Unwrapping)

The critical insight: if a .then() handler returns a Promise (or any thenable), the new promise should adopt that promise's state — not resolve with the promise object itself.

Loading editor...


Step 4 — .catch(), .finally(), and Static Methods

Loading editor...


What Interviewers Are Testing

  • State machine — understanding that a Promise can only transition once and in one direction
  • Microtask queue — knowing that handlers must be async (queueMicrotask, not setTimeout) and why this matters for execution order
  • The resolution procedure — the rule that makes chaining work: if a handler returns a thenable, adopt its state
  • Pass-through — when there's no handler for the current state, the value/reason propagates to the next .then()
  • finally semantics — it observes but doesn't transform; if it throws, the throw wins

Complexity

TimeSpace
ConstructionO(1)O(1)
.then()O(1)O(H) — H = handlers queued
SettlementO(H)O(1)

Interview Tips

  • Build the state machine first — write the constructor, #resolve, #reject before worrying about .then(). This shows structured thinking.
  • Name the resolution procedure — "When a handler returns a thenable, I need to adopt its state — this is the Promises/A+ resolution procedure." Naming it shows spec-level knowledge.
  • Explain queueMicrotask vs setTimeout — microtasks run before the next macrotask. Using setTimeout would break execution order. Interviewers listen for this distinction.
  • Don't worry about 100% spec compliance — a working implementation that handles chaining, pass-through, and error propagation correctly is sufficient. Mention you know the full spec exists.

Related Questions


Let's Connect

© 2026 Naveen Karthik // Built with React & MUI