Implement Custom Promise
A Promise is a fundamental concept in JavaScript for handling asynchronous operations. Understanding how to implement a custom Promise will deepen your understanding of asynchronous programming and help you in interviews.
Problem Statement
Implement a custom Promise class with the following methods:
constructor(executor)
- Initialize the promise with an executor functionthen(onFulfilled, onRejected)
- Handle successful and failed statescatch(onRejected)
- Handle only rejected statesfinally(onFinally)
- Execute regardless of statestatic resolve(value)
- Create a resolved promisestatic reject(reason)
- Create a rejected promisestatic all(promises)
- Wait for all promises to resolvestatic race(promises)
- Return the first settled promise
Solution
class CustomPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(callback => callback());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(callback => callback());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
return new CustomPromise((resolve, reject) => {
const handleFulfilled = () => {
try {
if (typeof onFulfilled === 'function') {
const result = onFulfilled(this.value);
resolve(result);
} else {
resolve(this.value);
}
} catch (error) {
reject(error);
}
};
const handleRejected = () => {
try {
if (typeof onRejected === 'function') {
const result = onRejected(this.reason);
resolve(result);
} else {
reject(this.reason);
}
} 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(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => {
onFinally();
return value;
},
reason => {
onFinally();
throw reason;
}
);
}
static resolve(value) {
return new CustomPromise(resolve => resolve(value));
}
static reject(reason) {
return new CustomPromise((resolve, reject) => reject(reason));
}
static all(promises) {
return new CustomPromise((resolve, reject) => {
const results = [];
let completedCount = 0;
if (promises.length === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
CustomPromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
reason => reject(reason)
);
});
});
}
static race(promises) {
return new CustomPromise((resolve, reject) => {
promises.forEach(promise => {
CustomPromise.resolve(promise).then(resolve, reject);
});
});
}
}
Usage Example
// Basic usage
const promise = new CustomPromise((resolve, reject) => {
setTimeout(() => {
resolve('Success!');
}, 1000);
});
promise
.then(value => {
console.log(value); // "Success!"
return value.toUpperCase();
})
.then(value => {
console.log(value); // "SUCCESS!"
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('Promise settled');
});
// Static methods
CustomPromise.all([
CustomPromise.resolve(1),
CustomPromise.resolve(2),
CustomPromise.resolve(3)
]).then(values => {
console.log(values); // [1, 2, 3]
});
CustomPromise.race([
new CustomPromise(resolve => setTimeout(() => resolve('First'), 100)),
new CustomPromise(resolve => setTimeout(() => resolve('Second'), 200))
]).then(value => {
console.log(value); // "First"
});
Key Concepts
- State Management: Promises have three states - pending, fulfilled, rejected
- Callback Queuing: Store callbacks when promise is pending, execute when settled
- Chaining:
then
returns a new promise for method chaining - Error Handling: Proper error propagation through the chain
- Asynchronous Execution: Use
setTimeout
to ensure callbacks run asynchronously
Common Interview Questions
- How does Promise chaining work internally?
- What's the difference between
Promise.all
andPromise.allSettled
? - How would you implement
Promise.allSettled
? - What happens if you don't handle promise rejections?
- How do you convert callback-based APIs to promises?
Related Topics
- Async/Await
- Generator Functions
- Event Loop
- Error Handling Patterns
- Promise Combinators