An Interview-focused explanation of Promise Polyfill in JavaScript which helps to understand both Functional and ES6 custom promise implementation.
Anuj Sharma
Last Updated Dec 27, 2024
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.
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.
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.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 !!
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 !!
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 !!
The functional implementation contains the custom promise function and constructor
then()
is executed at this state.catch()
execution happened in this state if it presents.executor
callback function. Resolve and reject functions passed by the promise implementation.successHandler
Array. This is the same array which got invoked when resolved it executed. When the promise state is FULFILLED, it invokes the handleSuccess Asynchronously. then(null, errorCallback)
. 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);
});
This ES6 implementation contains the custom promise implementation using ES6 classes.
static identifiers
as part of the class to manage the promise state.
then()
is executed at this state.catch()
execution happened in this state if it presents.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. catch() handlers or then() error handlers
which was part of either catch() callback function or then() callback function. successHandler
Array. This is the same array which got invoked when resolved it executed. When the promise state is FULFILLED, it invoks the handleSuccess Asynchronously. then(null, errorCallback)
. 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);
});
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.
Anuj Sharma
Last Updated Jan 4, 2025
Explore the most common ways to reverse a string in javascript including the most optimal way for frontend interviews with O(1) time complexity.
Anuj Sharma
Last Updated Jan 5, 2025
A comprehensive explanation about using javascript:void(0) in javascript. When to use javascript:void(0) and how it works with examples of using it with anchor tag.
Anuj Sharma
Last Updated Jan 2, 2025
Understand important web authorization techniques to enhance role-based authentication for any web application with popular techniques like Session & JSON Web Token (JWT)
Vivek Chavan
Last Updated Dec 23, 2024
You will get a clear understanding about working with any rest api and common concepts asked during interviews
Anuj Sharma
Last Updated Jan 9, 2025
Go through different ways to display dates using javascript date object. It covers examples of date object usage to understand the main concepts of javascript date object.
Anuj Sharma
Last Updated Dec 10, 2024
A brief explanation of Cross-Origin Resource Sharing (CORS) concept to enable client application accessing resources from cross domain and HTTP headers involved to enable resource access.
© 2024 FrontendGeek. All rights reserved