How do you implement a simple Promise?
A minimal Promise tracks a state and notifies callbacks when it settles.
Start in "pending" with empty callback lists.
Provide resolve and reject that set the state and value, then run queued callbacks.
then registers callbacks: run them now if already settled, or queue them.
Settle only once — ignore further resolve/reject calls.
Hard parts: chaining (each then returns a new promise) and always running callbacks asynchronously.
class MyPromise {
#state = 'pending';
#value = undefined;
#handlers = [];
constructor(executor) {
const resolve = (value) => {
if (this.#state !== 'pending') return;
this.#state = 'fulfilled';
this.#value = value;
this.#handlers.forEach(h => h.onFulfilled(value));
};
const reject = (reason) => {
if (this.#state !== 'pending') return;
this.#state = 'rejected';
this.#value = reason;
this.#handlers.forEach(h => h.onRejected(reason));
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
const handle = (value) => {
try {
const result = onFulfilled ? onFulfilled(value) : value;
resolve(result);
} catch (err) {
reject(err);
}
};
if (this.#state === 'fulfilled') {
queueMicrotask(() => handle(this.#value));
} else if (this.#state === 'rejected') {
queueMicrotask(() => {
if (onRejected) resolve(onRejected(this.#value));
else reject(this.#value);
});
} else {
this.#handlers.push({ onFulfilled: handle, onRejected: (r) => reject(r) });
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
}
// Test
const p = new MyPromise((resolve) => setTimeout(() => resolve(42), 100));
p.then(v => v * 2).then(v => console.log(v)); // 84The state machine starts as 'pending.' resolve/reject transition to 'fulfilled'/'rejected' (only once). If then is called before resolution, handlers are queued.
If called after, they execute immediately via queueMicrotask. then returns a new MyPromise enabling chaining. catch is sugar for then(null, onRejected).
This is an advanced coding question. Key parts: state machine (pending/fulfilled/rejected), resolve/reject only work once, handler queuing for async resolution, then returns a new Promise (chaining), and queueMicrotask for async execution.
This simplified version does not handle Promise resolution procedure (resolving with a thenable) but demonstrates the core concept.