Custom Implementations of Promise.all, Promise.any, Promise.allSettled, and Promise.race
Learn how JavaScript’s static Promise methods work by building them from scratch. This page covers custom implementations of Promise.all
,
Promise.allSettled
, Promise.any
, and Promise.race
— useful for understanding async behavior, debugging, and preparing for interviews.
Promise.all
The Promise.all()
static method takes an iterable of promises and returns a single Promise
that:
- fulfills when all input promises have fulfilled, returning an array of results in the original order
- rejects immediately if any promise rejects, with the first rejection reason
This example includes a custom implementation of Promise.all()
that resolves all promises and preserves their order, even if they complete at different times.
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
const results = [];
let completedPromises = 0;
for (let index = 0; index < promises.length; index++) {
Promise.resolve(promises[index])
.then((value) => {
results[index] = value;
console.log(value);
completedPromises += 1;
if (completedPromises === promises.length) {
resolve(results);
}
})
.catch(reject);
}
if (promises.length === 0) {
resolve([]);
}
});
}
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, "first");
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "second");
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 5000, "third");
});
myPromiseAll([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// second
// first
// third
// [ 'first', 'second', 'third' ]
Promise.allSettled
The Promise.allSettled()
method returns a promise that resolves after all promises in the input iterable have settled, regardless of whether they were fulfilled or rejected.
Each result is an object with a status
of either "fulfilled"
or "rejected"
and contains either the value
or the reason
.
This example demonstrates a custom implementation of Promise.allSettled()
using .then()
and .catch()
handlers for consistent result formatting.
const rejectHandler = (reason) => ({ status: "rejected", reason });
const resolveHandler = (value) => ({ status: "fulfilled", value });
Promise.customAllSettled = function (promises) {
const convertedPromises = promises.map((p) =>
Promise.resolve(p).then(resolveHandler, rejectHandler),
);
return Promise.all(convertedPromises);
};
Promise.customAllSettled([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Whoops!")), 2000),
),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
])
.then(console.info)
.catch(console.error);
// [
// { status: 'fulfilled', value: 1 },
// {
// status: 'rejected',
// reason: Error: Whoops!...
// },
// { status: 'fulfilled', value: 3 }
// ]
Promise.any
The Promise.any()
method returns a promise that fulfills as soon as any of the input promises is fulfilled.
If all promises reject, it rejects with an AggregateError
containing all rejection reasons.
This example demonstrates a custom implementation of Promise.any()
that resolves with the first successful result or throws an aggregate error if none succeed.
Promise.customAny = function (promises) {
return new Promise((resolve, reject) => {
const errors = [];
let remaining = promises.length;
if (remaining === 0) {
return reject(new AggregateError([], "All promises were rejected"));
}
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(resolve)
.catch((error) => {
errors[index] = error;
remaining -= 1;
if (remaining === 0) {
reject(new AggregateError(errors, "All promises were rejected"));
}
});
});
});
};
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, "quick"));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, "slow"));
const promises = [promise1, promise2, promise3];
Promise.customAny(promises).then((value) => console.log(value)); // quick
Promise.race
The Promise.race()
method returns a promise that settles as soon as the first input promise settles — whether it fulfills or rejects.
This makes it useful when you’re interested only in the fastest outcome, regardless of its result.
This example includes a custom implementation of Promise.race()
that resolves or rejects based on whichever promise finishes first.
Promise.customRace = function (promises) {
return new Promise((resolve, reject) => {
for (const promise of promises) {
Promise.resolve(promise).then(resolve, reject);
}
});
};
Promise.customRace([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Whoops!")), 2000),
),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(console.log); // 1