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.
Frontendgeek
Last Updated Apr 2, 2025
Advertisement
Jump to the topic
Promise.any()
takes multiple promises and returned the first one that resolved. If all promises reject, it returns an AggregateError
object containing all the error messages for all the rejected promises. Promise.any static method is quite useful in the cases where in the application we are just looking for the first success response and sequence doesn't matter in this case.
Real life Use Cases:
If your app serves images from multiple CDNs, you want the fastest CDN to load the image first.
In WebRTC-based applications, connect to the fastest signaling server for real-time communication.
const p1 = new Promise((resolve, reject) => setTimeout(reject, 1000, "Error from p1"));
const p2 = new Promise((resolve, reject) => setTimeout(resolve, 2000, "Success from p2"));
const p3 = new Promise((resolve, reject) => setTimeout(resolve, 3000, "Success from p3"));
Promise.any([p1, p2, p3])
.then((result) => console.log("First fulfilled promise:", result))
.catch((error) => console.error("All promises rejected:", error));
Output
// After 2 seconds
First fulfilled: Success from p2
Explanation:
p1
rejects after 1s.
p2
resolves after 2s, this is the first fulfilled promise that's why Promise.any()
returns its value.
p3
resolves after 3s, but since p2
promise already fulfilled, p3
is ignored.
When all the promises reject, Promise.any()
throws an AggregateError . AggregateError is an object which contains all the rejected responses as part of an errors array.
const p1 = Promise.reject("Error from p1");
const p2 = Promise.reject("Error from p2");
const p3 = Promise.reject("Error from p3");
Promise.any([p1, p2, p3])
.then((result) => console.log("First fulfilled:", result))
.catch((error) => {
console.log(error instanceof AggregateError); // true
console.error("All promises rejected:", error.errors);
});
output:
true
All promises rejected: [ 'Error from p1', 'Error from p2', 'Error from p3' ]
Here are the Promise.any expected functionalities that needs to be take care while implementing polyfill for Promise.any() .
❌ - Not executed, ✅ - Executed
Case1: Promise.any should return the first resolved value
Promise.any([
Promise.reject("Error A"),
Promise.resolve("Success B"), // First resolved promise
Promise.resolve("Success C"),
])
.then(result => console.log("✅ First resolved:", result))
.catch(error => console.error("❌ Unexpected error:", error));
// Output
✅ First resolved: Success B
Returns the first resolved promise, others got ignored.
Promise.any([
new Promise(res => setTimeout(res, 500, "Success A")), // resolved first
new Promise((res, rej) => setTimeout(rej, 300, "Error B")),
new Promise(res => setTimeout(res, 600, "Success C")), // resolved second
])
.then(result => console.log("✅ First resolved:", result))
.catch(error => console.error("❌ Unexpected error:", error));
// Output
✅ First resolved: Success A
Case2: When all promise rejected or passed iterable is empty, Promise.any should throw AggregateError
Promise.any([
Promise.reject("Error A"),
Promise.reject("Error B"),
])
.then(result => console.log("❌ Success:", result))
.catch(error => console.log("✅ AggregateError caught:", error.errors));
// Output
✅ AggregateError caught: ["Error A", "Error B"]
Empty iterable
Promise.any([])
.then(result => console.log("❌ Unexpected success:", result))
.catch(error => console.log("✅ AggregateError caught:", error.errors));
// Output
✅ AggregateError caught: []
Case3: In case of already resolved promise it directly returns that first resolved promise
Promise.any([
Promise.resolve("Immediate Success1"),
Promise.resolve("Immediate Success2"),
Promise.reject("Immediate Reject")
])
.then(result => console.log("✅ Immediate resolved:", result))
.catch(error => console.error("❌ Unexpected error:", error));
// Output
✅ Immediate resolved: Immediate Success1
Case4: In case of non-promise iterable, Promise.any() returns the non-promise values if that is the first one to process, before any resolved promise
//--------Example 1--------------
Promise.any([
Promise.reject("Error"),
Promise.resolve("world"),
"Non-promise"
])
.then(result => console.log("✅ First resolved:", result))
.catch(error => console.error("❌ Unexpected error:", error));
//Output
✅ First resolved: world
//--------Example 2--------------
Promise.any([
"Non-promise",
Promise.reject("Error"),
Promise.resolve("world")
])
.then(result => console.log("✅ First resolved:", result))
.catch(error => console.error("❌ Unexpected error:", error));
//Output
✅ First resolved: Non-promise
Here are the 3 steps with explanation, those are required to implement the polyfill of Promise.any()
Step 1: Handle empty iterable input
If Promise.any([])
is called, it immediately rejects with AggregateError
.
if (!Array.isArray(promises) || promises.length === 0) {
reject(new AggregateError([], "All promises were rejected"));
return;
}
Step 2: Loop over all the input promises
Maintain an array of rejections to track the all rejection messages those will pass to AggregatorError, along with the length of the input promises.
let rejections = [];
let pending = promises.length;
promises.forEach((promise, index) => {
// Loop over
})
Step 3: Pass each promise to Promise.resolve()
In the loop pass each promise to Promise.resolve(), and track the fulfilled and rejected promises. In case of resolved promise, first resolved promise will be returned, other wise in case of rejected promises push the rejected error message for AggregatorError and keep track of the length of the promises. If all the promises found as rejected then an AggregatorError object should be returned with all the rejected error messages with rejections array.
Promise.resolve(promise)
.then(resolve)
.catch((err) => {
rejections[index] = err;
pending--;
if (pending === 0) {
reject(new AggregateError(rejections, "All promises were rejected"));
}
});
Promise.customAny = function (promises) {
return new Promise((resolve, reject) => {
// Step 1
if (!Array.isArray(promises) || promises.length === 0) {
reject(new AggregateError([], "All promises were rejected"));
return;
}
// Step 2
let rejections = [];
let pending = promises.length;
promises.forEach((promise, index) => {
// Step 3
Promise.resolve(promise)
.then(resolve) // ✅ First fulfilled promise resolves
.catch((err) => {
rejections[index] = err;
pending--;
// ❌ If all promises are rejected, return AggregateError
if (pending === 0) {
reject(new AggregateError(rejections, "All promises were rejected"));
}
});
});
});
};
// Test
Promise.customAny([
new Promise(res => setTimeout(res, 500, "Success A")), // resolved first
new Promise((res, rej) => setTimeout(rej, 300, "Error B")),
new Promise(res => setTimeout(res, 600, "Success C")), // resolved second
])
.then(result => console.log("✅ First resolved:", result))
.catch(error => console.error("❌ Unexpected error:", error));
// Output
✅ First resolved: Success A
Advertisement
Advertisement
Advertisement
Alok Kumar Giri
Last Updated Apr 28, 2025
Code snippet examples which will help to grasp the concept of Hoisting in JavaScript, with solutions to understand how it works behind the scene.
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 Jan 29, 2025
Understand the difference between HTTP/2 vs HTTP/1.1 based on the various parameters, which helps to understand the improvement areas of HTTP/2 over HTTP 1.1
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.
Anuj Sharma
Last Updated Jan 16, 2025
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.
Anuj Sharma
Last Updated Jan 9, 2025
Learn the best & quickest way to format phone number in JavaScript with or without country codes. This will help websites to show the phone numbers in a more human-readable format.
© 2024 FrontendGeek. All rights reserved