Deep Dive into Implementing JavaScript Promise: Static Methods, resolve, reject, all & race
This article walks through a step‑by‑step implementation of JavaScript's Promise, covering prototype methods, static helpers like resolve, reject, all, and race, with detailed code examples, execution flow explanations, and a summary of the underlying observer pattern.
Promise is a core solution for asynchronous programming in JavaScript, standardized in ES6. This series aims to help readers understand the inner workings of Promise by progressively implementing it, visualizing the process with diagrams, examples, and animations.
Prototype Method Implementation Recap
The previous sections built the prototype methods (then, catch, finally) and added state handling (pending, fulfilled, rejected) and result storage. The current implementation looks like this:
class Promise {
callbacks = [];
state = 'pending'; // increase state
value = null; // store result
constructor(fn) {
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve,
reject
});
});
}
catch(onError) { return this.then(null, onError); }
finally(onDone) {
if (typeof onDone !== 'function') return this.then();
let Promise = this.constructor;
return this.then(
value => Promise.resolve(onDone()).then(() => value),
reason => Promise.resolve(onDone()).then(() => { throw reason })
);
}
_handle(callback) {
if (this.state === 'pending') { this.callbacks.push(callback); return; }
let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
if (!cb) { cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; cb(this.value); return; }
let ret;
try { ret = cb(this.value); cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; }
catch (error) { ret = error; cb = callback.reject; }
finally { cb(ret); }
}
_resolve(value) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') { then.call(value, this._resolve.bind(this), this._reject.bind(this)); return; }
}
this.state = 'fulfilled'; // change state
this.value = value; // save result
this.callbacks.forEach(callback => this._handle(callback));
}
_reject(error) {
this.state = 'rejected';
this.value = error;
this.callbacks.forEach(callback => this._handle(callback));
}
}Static Methods Overview
Beyond prototype methods, Promise provides static helpers: Promise.resolve, Promise.reject, Promise.all, and Promise.race. Their implementations follow similar patterns.
Promise.resolve & Promise.reject
Promise.resolvewraps non‑Promise values into a resolved Promise. If the argument is already a Promise, it returns it unchanged. Example:
Promise.resolve('foo') // equivalent to new Promise(resolve => resolve('foo'))When the argument is a thenable object, Promise.resolve adopts its state by calling its then method:
let thenable = { then: function(onFulfilled) { onFulfilled(42); } };
let p1 = Promise.resolve(thenable);
p1.then(value => console.log(value)); // 42If the argument is a primitive or a non‑thenable object, a new resolved Promise is returned. Calling Promise.resolve() with no argument yields a Promise already resolved with undefined.
The source code for Promise.resolve:
static resolve(value) {
if (value && value instanceof Promise) { return value; }
else if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise(resolve => { then(resolve); });
} else if (value) {
return new Promise(resolve => resolve(value));
} else {
return new Promise(resolve => resolve());
}
} Promise.rejectalways returns a Promise in the rejected state, regardless of its argument.
Promise.all
Promise.allaccepts an array of Promises (or values) and resolves when every entry is fulfilled, returning an array of results in the same order:
const p1 = new Promise(resolve => setTimeout(() => resolve('p1'), 1000));
const p2 = new Promise(resolve => setTimeout(() => resolve('p2'), 5000));
Promise.all([p1, p2]).then(rets => console.log(rets)); // ['p1','p2']Implementation:
static all(promises) {
return new Promise((resolve, reject) => {
let fulfilledCount = 0;
const itemNum = promises.length;
const rets = Array.from({ length: itemNum });
promises.forEach((promise, index) => {
Promise.resolve(promise).then(result => {
fulfilledCount++;
rets[index] = result;
if (fulfilledCount === itemNum) { resolve(rets); }
}, reason => reject(reason));
});
});
}Promise.race
Promise.racereturns a Promise that settles as soon as any of the supplied Promises settles (fulfilled or rejected). Example:
const p1 = new Promise(resolve => setTimeout(() => resolve('p1'), 1000));
const p2 = new Promise(resolve => setTimeout(() => resolve('p2'), 5000));
Promise.race([p1, p2]).then(ret => console.log(ret)); // 'p1'Implementation:
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(
value => resolve(value),
reason => reject(reason)
);
}
});
}Key Takeaways
The core of Promise relies on the observer pattern: then and catch register observers, while the internal state machine (pending → fulfilled/rejected) notifies them via resolve or reject. Understanding that then merely registers callbacks and the actual execution happens inside resolve clarifies the flow.
Although the presented implementation demonstrates the main ideas, it does not fully satisfy every requirement of the Promise/A+ specification.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
