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:
.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.- The resolution procedure — when a
.then()handler returns a thenable (another Promise, or any object with a.thenmethod), 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:
- State machine — constructor,
#resolve,#reject - Handler queue — store
.then()callbacks for when the promise settles - Async execution — use
queueMicrotaskso handlers always run after current synchronous code .then()returns a new Promise — the result of the handler becomes the new promise's value- Resolution procedure — if a handler returns a thenable, adopt its state
.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, notsetTimeout) 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() finallysemantics — it observes but doesn't transform; if it throws, the throw wins
Complexity
| Time | Space | |
|---|---|---|
| Construction | O(1) | O(1) |
.then() | O(1) | O(H) — H = handlers queued |
| Settlement | O(H) | O(1) |
Interview Tips
- Build the state machine first — write the constructor,
#resolve,#rejectbefore 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
queueMicrotaskvssetTimeout— microtasks run before the next macrotask. UsingsetTimeoutwould 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.