JS Machine Coding Questions — Implementations You Must Know
Promise — Build from Scratch
- Implement a MyPromise class with resolve, reject, .then, and .catch.
- Key insight: Store state (pending/fulfilled/rejected) and value. Queue .then callbacks — run them when the promise settles. Chaining works by returning a new MyPromise from each .then.
- Gotcha: A promise can only transition once — guard with if (state !== 'pending') return.
- Gotcha: If .then callback itself throws, the returned promise must reject.
- Gotcha: Real Promises run callbacks asynchronously (microtask queue) — simplified version runs them synchronously.
1class MyPromise {
2 constructor(executor) {
3 this.state = "pending";
4 this.value = undefined;
5 this.onFulfilledCbs = [];
6 this.onRejectedCbs = [];
7
8 const resolve = (value) => {
9 if (this.state !== "pending") return;
10 this.state = "fulfilled";
11 this.value = value;
12 this.onFulfilledCbs.forEach(cb => cb(value));
13 };
14 const reject = (reason) => {
15 if (this.state !== "pending") return;
16 this.state = "rejected";
17 this.value = reason;
18 this.onRejectedCbs.forEach(cb => cb(reason));
19 };
20 try { executor(resolve, reject); }
21 catch (e) { reject(e); }
22 }
23
24 then(onFulfilled, onRejected) {
25 return new MyPromise((resolve, reject) => {
26 const handleFulfill = (value) => {
27 try { resolve(onFulfilled ? onFulfilled(value) : value); }
28 catch (e) { reject(e); }
29 };
30 const handleReject = (reason) => {
31 try { resolve(onRejected ? onRejected(reason) : (() => { throw reason; })()); }
32 catch (e) { reject(e); }
33 };
34 if (this.state === "fulfilled") handleFulfill(this.value);
35 else if (this.state === "rejected") handleReject(this.value);
36 else {
37 this.onFulfilledCbs.push(handleFulfill);
38 this.onRejectedCbs.push(handleReject);
39 }
40 });
41 }
42
43 catch(onRejected) { return this.then(null, onRejected); }
44 static resolve(value) { return new MyPromise(res => res(value)); }
45 static reject(reason) { return new MyPromise((_, rej) => rej(reason)); }
46}