Promise Polyfill in JavaScript - Step by Step Explanation
An Interview-focused explanation of Promise Polyfill in JavaScript which helps to understand both Functional and ES6 custom promise implementation.
Anuj Sharma
Last Updated Jun 15, 2026

Promise polyfill or custom promise implementation is an essential concept to understand for frontend interviews. Implementation of promise polyfill is complex and in this guide, we will try to simplify this complex implementation by providing a step-by-step explanation.
In this Post
- How does Promise in JavaScript work?
- Functional Implementation of Promise in JavaScript
- ES6 Implementation of Promise in JavaScript
How does Promise in JavaScript work?
Before diving deep into the promise polyfill in javascript it is important to understand the execution of the promise and different use cases related to then, catch, resolve and reject. This will help write custom promises and test the polyfill against the required use cases.
Promise example with resolve()
Example
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Call is resolved');
}, 1000);
});
promise.then(value => {
console.log('Value after 1 sec - ', value);
});
Output:
Value after 1 sec - Call is resolved
Promise example with reject()
Example 1: Handle rejected promise using Promise.then()
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Error happened !!'));
}, 1000);
});
promise2.then(
value => {
console.log('Value after 1 sec - ', value);
},
error => {
console.log('Error - ', error.message);
}
);
Output
Error - Error happened !!
Example 2: Handle rejected promise using Promise.catch()
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Error happened !!'));
}, 1000);
});
promise2
.then(value => {
console.log('Value after 1 sec - ', value);
})
.catch(
error => {
console.log('Error from catch - ', error.message);
}
)
Output:
Error from catch - Error - Error happened !!
Example 3: Ignore catch if the error has already been handled by then previously in the code.
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Error happened !!'));
}, 1000);
});
promise2.then(
value => {
console.log('Value after 1 sec - ', value);
},
error => {
console.log('Error - ', error.message);
}
).catch(
error => {
console.log('Error from catch - ', error.message);
}
)
Output
Error - Error happened !!
Functional Implementation of Promise Polyfill in JavaScript
The functional implementation contains the custom promise function and constructor
Step by Step Explanation
- Step 1: Create the required identifiers to manage the promise state.
- PENDING: This is the initial state of the promise when the promise has been created but the associated asynchronous function has not yet succeeded or failed.
- FULFILLED: This state represents the successful completion of the promise.
then()is executed at this state. - REJECTED: Represents the failed promise state. The
catch()execution happened in this state if it presents. - successHandlers: Array to catch all the success callbacks passed as part of then().
- errorHandlers: Array to capture the catch callback.
- Step 2: The whole execution of CustomPromise started with executing the passed
executorcallback function. Resolve and reject functions passed by the promise implementation. - resolve() implementation: resolve() invoked as part of the executor. It invokes only when the current state is "PENDING". As part of its invocation, it changes the "PENDING" state to "FULFILLED" and execute all the success handlers which was part of the then callback function.
- reject() implementation: reject() invoked as part of the executor. It invokes only when the current state is "PENDING". As part of its invocation, it changes the "PENDING" state to "REJECTED" and execute all the catch() handlers or then() error handlers which was part of either catch() callback function or then() callback function.
- then() implementation: This is the most crucial implementation as part of Promise. It return another promise to enable promise chaining. As part of the first step of invocation it return a new instance of the promise.
At the start when the promise state is pending, it keeps all the success callback functions as part ofsuccessHandlerArray. This is the same array which got invoked when resolved it executed. When the promise state is FULFILLED, it invokes the handleSuccess Asynchronously. - catch() implementation: Catch implementation is straight forward, catch is just a syntactic sugar on top of the then() where we invoke the then() with successCallback as null and only pass the errorCallback like
then(null, errorCallback).
Functional Implementation Code of Promise Polyfill
function CustomPromise(executor) {
// Step 1
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
let state = PENDING;
let value = null;
// Store the success callbacks
let successHandlers = [];
// Store the error catch callbacks
let errorHandlers = [];
function resolve(val) {
if (state === PENDING) {
state = FULFILLED;
value = val;
// Execute all success handlers
successHandlers.forEach(fn => fn(value));
}
}
function reject(reason) {
if (state === PENDING) {
state = REJECTED;
value = reason;
// Execute all error handlers
errorHandlers.forEach(cb => cb(value));
}
}
// "then" method returns a promise
this.then = function (callback) {
return new CustomPromise(function (resolve, reject) {
// Handle the success callback asynchronously.
function handleSuccess() {
try {
const result = callback ? callback(value) : value;
//check if the result of then is also a promise
if (result && typeof result.then === 'function') {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
}
}
if (state === FULFILLED) {
// Call handleSuccess asynchronously
setTimeout(handleSuccess, 0);
} else if (state === PENDING) {
successHandlers.push(handleSuccess);
}
});
};
// The catch method to add error handlers (syntactic sugar for .then(null, errorCallback))
this.catch = function (errorCallback) {
return this.then(null, errorCallback);
};
// Step 2
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
Example to execute function based CustomPromise Polyfill
const executor = (resolve, reject) => {
console.log('generating number...');
setTimeout(() => {
let randomNumber = Math.floor(Math.random() * 10);
if (randomNumber < 10) {
resolve(`Random number generated successfully ${randomNumber}.`);
} else {
reject('Failed to generate number less than 10 !!');
}
}, 1000);
};
const generateNumber = new CustomPromise(executor);
generateNumber.then(result => {
// Random number generated successfully <random Number>
console.log(result);
});
generateNumber.catch(error => {
// Failed to generate a number less than 10 !!
console.log(error);
});
ES6 Implementation of Promise Polyfill in JavaScript
This ES6 implementation contains the custom promise implementation using ES6 classes.
Step by Step Explanation
- Step 1: Declare required Identifiers - Create the required
static identifiersas part of the class to manage the promise state.- PENDING: This is the initial state of the promise when the promise has been created but the associated asynchronous function has not yet succeeded or failed.
- FULFILLED: This state represents the successful completion of the promise.
then()is executed at this state. - REJECTED: Represents the failed promise state. The
catch()execution happened in this state if it presents. - successHandlers: Array to catch all the success callbacks passed as part of then().
- errorHandlers: Array to capture the catch callback.
- Step 2: Invoke executor function - The whole execution of CustomPromise started with the
class constructor, which takes the executor function as input and pass both resolve and reject functions while invoking the same. Invocation of resolve and reject will determine whether promise is FULFILLED or REJECTED. - resolve() implementation: resolve() invoked as part of the executor in constructor. It invokes only when the current state is "PENDING". As part of its invocation, it changes the "PENDING" state to "FULFILLED" and execute all the success handlers which was part of the then callback function.
- reject() implementation: reject() invoked as part of the executor. It invokes only when the current state is "PENDING". As part of its invocation, it changes the "PENDING" state to "REJECTED" and execute all the
catch() handlers or then() error handlerswhich was part of either catch() callback function or then() callback function. - then() implementation: This is the most crucial implementation as part of Promise. It return another promise to enable promise chaining. As part of the first step of invocation it return a new instance of the promise.
At the start when the promise state is pending, it keeps all the success callback functions as part ofsuccessHandlerArray. This is the same array which got invoked when resolved it executed. When the promise state is FULFILLED, it invoks the handleSuccess Asynchronously. - catch() implementation: Catch implementation is straight forward, catch is just a syntactic sugar on top of the then() where we invoke the then() with successCallback as null and only pass the errorCallback like
then(null, errorCallback).
ES6 Implementation code of Promise Polyfill
class CustomPromise {
// Step 1
static STATE = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected',
};
constructor(executor) {
this.state = CustomPromise.STATE.PENDING;
this.value = null;
this.successHandlers = [];
this.errorHandlers = [];
const resolve = (val) => {
if (this.state === CustomPromise.STATE.PENDING) {
this.state = CustomPromise.STATE.FULFILLED;
this.value = val;
this.successHandlers.forEach(callback => callback(this.value));
}
};
const reject = (reason) => {
if (this.state === CustomPromise.STATE.PENDING) {
this.state = CustomPromise.STATE.REJECTED;
this.value = reason;
this.errorHandlers.forEach(callback => callback(this.value));
}
};
// Step 2
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(callback) {
return new CustomPromise((resolve, reject) => {
const handleSuccess = ()=>{
try{
const result = callback ? callback(this.value) : this.value;
if(result && typeof result.then === 'function'){
result.then(resolve, reject)
}else{
resolve(result)
}
}catch(err){
reject(err);
}
}
if (this.state === CustomPromise.STATE.FULFILLED) {
setTimeout(handleSuccess, 0);
} else if (this.state === CustomPromise.STATE.PENDING) {
this.successHandlers.push(handleSuccess);
}
});
}
catch(errorCallback) {
return this.then(null, errorCallback);
}
}
Example usage of ES6 based CustomPromise polyfill
const executor = (resolve, reject) => {
console.log('generating number...');
setTimeout(() => {
let randomNumber = Math.floor(Math.random() * 10);
if (randomNumber < 7) {
resolve(`Random number generated successfully ${randomNumber}.`);
} else {
reject('Failed to generate number less than 10 !!');
}
}, 1000);
};
const generateNumber = new CustomPromise(executor);
generateNumber.then(result => {
// Random number generated successfully <random Number>
console.log(result);
});
generateNumber.catch(error => {
// Failed to generate a number less than 10 !!
console.log(error);
});
Final Note:
Implementing Promise polyfill in JavaScript is the most common interview question in experienced frontend interviews. Its important to map the actual working of the polyfill with the implementation of the Promise Polyfill to understand it fully. This way you need not to keep the whole implementation in mind rather you can derive the whole Promise Polyfill implementation on the fly using the usage pattern of the Promise in JavaScript.
Hope you will be able to understand the Promise Polyfill in JavaScript implementation in depth to Ace the next frontend interview.
What Next
1️⃣ Promise.all Polyfill in JavaScript Explained
2️⃣ Promise.allSettled in JavaScript explained
3️⃣ Promise.race polyfill in JavaScript explained
4️⃣ Promise.any polyfill in JavaScript explained
A seasoned Sr. Engineering Manager at GoDaddy (Ex-Dell) with over 12+ years of experience in the frontend technologies. A frontend tech enthusiast passionate building SaaS application to solve problem. Know more about me 🚀
Learn Next
Comments
Kanhav sukhwal
kanhavsukhwal@gmail.com
27 Sept, 2025
hi
Kanhav sukhwal
kanhavsukhwal@gmail.com
27 Sept, 2025
How are the callbacks are being added in errorHandlers array , there doesn't seem any part doing that.
Share your expertise
Publish a blog or quick notes on topics you know well — your write-up could be the answer someone needs before their next frontend interview.
Build your portfolio
Help the community
Sharpen your skills
Earn goodies
Other Related Blogs
clearTimeout polyfill in JavaScript - Detailed Explanation
Anuj Sharma
Last Updated Jun 15, 2026
Understand the implementation of the clearTimeout polyfill in JavaScript with a detailed explanation of each and every step.
Promise.race Polyfill in Javascript - Detailed Explanation
Anuj Sharma
Last Updated Jun 15, 2026
Detailed step-by-step explanation of Promise.race polyfill in javascript to understand its internal working and handling of race conditions among promises.
setInterval polyfill in JavaScript - Detailed Explanation
Anuj Sharma
Last Updated Jun 15, 2026
Understand the implementation of the setInterval polyfill in JavaScript with a detailed explanation of each and every step.
setTimeout Polyfill in JavaScript - Detailed Explanation
Anuj Sharma
Last Updated Jun 15, 2026
Explore the implementation of setTimeout in JavaScript with a detailed explanation for every step. Understand all scenarios expected to implement the setTimeout polyfill.
Promise.all Polyfill in JavaScript - Detailed Explanation [For Interviews]
Anuj Sharma
Last Updated Jun 5, 2026
Deep dive into promise.all polyfill in javascript will help to understand the working of parallel promise calls using Promise.all and its implementation to handle parallel async API calls.
Polyfill for Async Await in JavaScript - Step by Step Explanation
Anuj Sharma
Last Updated Feb 21, 2026
Understand polyfill for Async Await in JavaScript with a step-by-step explanation. This helps in understanding the internal functioning of Async Await in JavaScript.
Promise.any Polyfill in JavaScript - Detailed Explanation
Frontendgeek
Last Updated Sep 18, 2025
A step-by-step detailed explanation of Promise.any polyfill in JavaScript to understand the internal implementation to handle race conditions among promises to result in a single resolved promise.
Promise.allSettled Polyfill in JavaScript - Step by Step Explanation
Frontendgeek
Last Updated Sep 18, 2025
Deep dive into Promise.allSettled Polyfill in JavaScript, which helps to understand the internal implementation of Promise.allSettled method to handle parallel calls with failure cases.
