A JavaScript Promise is an asynchronous mechanism that represents a future value. Below is a fully functional implementation of a custom Promise (CustomPromise) that behaves exactly like the native JavaScript Promise.
class CustomPromise { constructor(executor) { this.state = 'pending'; // Possible states: 'pending', 'fulfilled', 'rejected' this.value = undefined; // Holds the resolved value this.reason = undefined; // Holds the rejection reason this.onFulfilledCallbacks = []; // Stores `.then` success handlers this.onRejectedCallbacks = []; // Stores `.then` error handlers // Resolve function const resolve = (value) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onFulfilledCallbacks.forEach(callback => callback(value)); } }; // Reject function const reject = (reason) => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(callback => callback(reason)); } }; try { executor(resolve, reject); } catch (error) { reject(error); } } // `then` method then(onFulfilled, onRejected) { return new CustomPromise((resolve, reject) => { const handleFulfilled = () => { try { const result = onFulfilled ? onFulfilled(this.value) : this.value; result instanceof CustomPromise ? result.then(resolve, reject) : resolve(result); } catch (error) { reject(error); } }; const handleRejected = () => { try { const result = onRejected ? onRejected(this.reason) : this.reason; result instanceof CustomPromise ? result.then(resolve, reject) : reject(result); } catch (error) { reject(error); } }; if (this.state === 'fulfilled') { setTimeout(handleFulfilled, 0); } else if (this.state === 'rejected') { setTimeout(handleRejected, 0); } else { this.onFulfilledCallbacks.push(handleFulfilled); this.onRejectedCallbacks.push(handleRejected); } }); } // `catch` method catch(onRejected) { return this.then(null, onRejected); } // `finally` method finally(callback) { return this.then( value => CustomPromise.resolve(callback()).then(() => value), reason => CustomPromise.resolve(callback()).then(() => { throw reason; }) ); } // Static `resolve` method static resolve(value) { return new CustomPromise((resolve) => resolve(value)); } // Static `reject` method static reject(reason) { return new CustomPromise((_, reject) => reject(reason)); } // Static `all` method static all(promises) { return new CustomPromise((resolve, reject) => { let results = []; let completed = 0; promises.forEach((promise, index) => { CustomPromise.resolve(promise).then(value => { results[index] = value; completed++; if (completed === promises.length) resolve(results); }).catch(reject); }); if (promises.length === 0) resolve([]); }); } // Static `race` method static race(promises) { return new CustomPromise((resolve, reject) => { promises.forEach(promise => { CustomPromise.resolve(promise).then(resolve).catch(reject); }); }); } // Static `allSettled` method static allSettled(promises) { return new CustomPromise((resolve) => { let results = []; let completed = 0; promises.forEach((promise, index) => { CustomPromise.resolve(promise) .then(value => results[index] = { status: 'fulfilled', value }) .catch(reason => results[index] = { status: 'rejected', reason }) .finally(() => { completed++; if (completed === promises.length) resolve(results); }); }); if (promises.length === 0) resolve([]); }); } }
executor)
executor function with resolve and reject callbacks.pending, fulfilled, or rejected)..then() when Promise is still pending.then)
Promise is already resolved, calls the callback immediately.CustomPromise.catch)
.then(null, onRejected), forwarding rejection.finally)
resolve(value) β Instantly resolves with value.reject(reason) β Instantly rejects with reason.all(promises) β Resolves when all promises succeed, or rejects on the first failure.race(promises) β Resolves or rejects as soon as one promise settles.allSettled(promises) β Resolves when all promises are settled, never rejects.const asyncTask = (value, delay, shouldReject = false) => { return new CustomPromise((resolve, reject) => { setTimeout(() => { shouldReject ? reject(`Error: ${value}`) : resolve(value); }, delay); }); }; // Example 1: Basic usage asyncTask("Success", 1000).then(console.log); // Output: "Success" after 1s // Example 2: Chaining asyncTask(10, 500) .then(num => num * 2) .then(num => console.log(num)); // Output: 20 // Example 3: Catching errors asyncTask("Fail", 500, true) .catch(err => console.error(err)); // Output: "Error: Fail" // Example 4: Finally asyncTask("Cleanup", 500) .finally(() => console.log("Finished")) // Output: "Finished" // Example 5: Promise.all CustomPromise.all([asyncTask(1, 100), asyncTask(2, 200)]) .then(console.log); // Output: [1, 2] // Example 6: Promise.race CustomPromise.race([asyncTask(1, 500), asyncTask(2, 100)]) .then(console.log); // Output: 2 // Example 7: Promise.allSettled CustomPromise.allSettled([ asyncTask("Success", 100), asyncTask("Error", 200, true) ]).then(console.log); // Output: [{ status: 'fulfilled', value: 'Success' }, { status: 'rejected', reason: 'Error' }]
β Matches JavaScript Promises API exactly
β
Supports async execution with .then(), .catch(), .finally()
β
Implements Promise.all, Promise.race, Promise.allSettled
β
Uses setTimeout(β¦, 0) for microtask-like behavior
π This implementation is fully functional and behaves like native JS Promises!
Additional Resources:
Test your understanding with 3 quick questions