Promise.any()
Basic Concept of Promise.any()
Promise.any() is a new method introduced in ECMAScript 2021 (ES12) for handling multiple Promise instances. It takes an iterable collection of Promise objects and returns a new Promise. As soon as any one of the Promises is fulfilled, this new Promise resolves immediately with the value of the first successful Promise. If all Promises are rejected, the returned Promise is rejected with an AggregateError object containing all the rejection reasons.
const promise1 = Promise.reject("Error 1");
const promise2 = Promise.resolve("Success 2");
const promise3 = new Promise((resolve) => setTimeout(resolve, 100, "Success 3"));
Promise.any([promise1, promise2, promise3])
.then((value) => console.log(value)) // Output: "Success 2"
.catch((error) => console.error(error));
Differences from Promise.all() and Promise.race()
Promise.any() differs significantly from Promise.all() and Promise.race():
- Promise.all(): Waits for all Promises to succeed or the first failure.
- Promise.race(): Adopts the first settled Promise (whether fulfilled or rejected).
- Promise.any(): Adopts the first fulfilled Promise and only rejects if all fail.
// Promise.all() example
Promise.all([Promise.resolve(1), Promise.resolve(2)])
.then(values => console.log(values)); // [1, 2]
// Promise.race() example
Promise.race([Promise.reject("Error"), Promise.resolve("Success")])
.catch(err => console.log(err)); // "Error"
// Promise.any() example
Promise.any([Promise.reject("Error"), Promise.resolve("Success")])
.then(value => console.log(value)); // "Success"
Error Handling Mechanism
When all passed Promises are rejected, Promise.any() throws an AggregateError object, which includes an errors
property containing an array of all rejection reasons.
const promises = [
Promise.reject("Error 1"),
Promise.reject("Error 2"),
Promise.reject("Error 3")
];
Promise.any(promises)
.catch(error => {
console.log(error instanceof AggregateError); // true
console.log(error.errors); // ["Error 1", "Error 2", "Error 3"]
});
Practical Use Cases
Multi-Source Data Fetching
When fetching the same data from multiple sources, Promise.any() can be used to get the fastest response:
const fetchFromSource1 = fetch('https://source1.com/data');
const fetchFromSource2 = fetch('https://source2.com/data');
const fetchFromSource3 = fetch('https://source3.com/data');
Promise.any([fetchFromSource1, fetchFromSource2, fetchFromSource3])
.then(response => response.json())
.then(data => console.log('Received data:', data))
.catch(error => console.error('All sources failed:', error.errors));
Fallback Service Calls
In a microservices architecture, Promise.any() can be used to implement service degradation:
async function getServiceData() {
const primaryService = callPrimaryService().catch(() => { throw "Primary failed" });
const secondaryService = callSecondaryService().catch(() => { throw "Secondary failed" });
const fallbackService = callFallbackService();
return Promise.any([primaryService, secondaryService, fallbackService]);
}
getServiceData()
.then(data => updateUI(data))
.catch(error => showError(error));
Browser Compatibility and Polyfill
Browser support for Promise.any():
- Chrome 85+
- Firefox 79+
- Safari 14+
- Edge 85+
- Node.js 15.0.0+
For unsupported environments, use the following polyfill:
if (!Promise.any) {
Promise.any = function(promises) {
return new Promise((resolve, reject) => {
let rejections = [];
let pending = promises.length;
if (pending === 0) {
reject(new AggregateError([], "All promises were rejected"));
return;
}
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(resolve)
.catch(error => {
rejections[index] = error;
pending--;
if (pending === 0) {
reject(new AggregateError(rejections, "All promises were rejected"));
}
});
});
});
};
}
Performance Considerations and Best Practices
When using Promise.any(), note the following:
- Canceling pending Promises: Other Promises continue executing after resolution.
- Memory consumption: A large number of Promises may consume significant memory.
- Error collection: AggregateError may contain extensive error information.
Optimization example:
// Use AbortController to cancel pending requests
async function fetchFastest(urls, signal) {
const controller = new AbortController();
signal?.addEventListener('abort', () => controller.abort());
const requests = urls.map(url =>
fetch(url, { signal: controller.signal })
.then(response => {
controller.abort(); // Cancel other requests
return response;
})
);
return Promise.any(requests);
}
// Usage
const controller = new AbortController();
fetchFastest(['url1', 'url2', 'url3'], controller.signal)
.then(response => console.log(response))
.catch(error => console.error(error));
// Timeout cancellation
setTimeout(() => controller.abort(), 5000);
Integration with Other Async Patterns
Promise.any() can be combined with other async patterns:
With async/await
async function loadContent(sources) {
try {
const responses = sources.map(source => fetch(source));
const firstResponse = await Promise.any(responses);
return await firstResponse.json();
} catch (error) {
if (error instanceof AggregateError) {
console.error('All sources failed:', error.errors);
} else {
console.error('Unexpected error:', error);
}
throw error;
}
}
With Promise.allSettled()
async function getDataWithFallback(primary, fallbacks) {
const allPromises = [primary, ...fallbacks];
const result = await Promise.any(allPromises);
// Log all Promise states
const settlements = await Promise.allSettled(allPromises);
logSettlements(settlements);
return result;
}
Special Cases and Edge Scenarios
Empty Array Input
When passed an empty array, Promise.any() rejects synchronously:
Promise.any([])
.catch(error => {
console.log(error instanceof AggregateError); // true
console.log(error.errors); // []
console.log(error.message); // "All promises were rejected"
});
Non-Promise Values
Promise.any() converts non-Promise values into resolved Promises:
Promise.any([1, Promise.resolve(2), Promise.reject(3)])
.then(value => console.log(value)); // 1
Synchronous Errors
If a Promise in the iterable throws an error synchronously:
const promise = new Promise(() => { throw new Error("Sync error"); });
Promise.any([promise])
.catch(error => {
console.log(error instanceof AggregateError); // true
console.log(error.errors[0] instanceof Error); // true
});
Usage in Node.js
In Node.js, Promise.any() can be used for filesystem operations:
const fs = require('fs').promises;
const path = require('path');
async function findConfigFile(locations) {
const filePromises = locations.map(location =>
fs.access(location)
.then(() => location)
.catch(() => { throw `${location} not found` })
);
return Promise.any(filePromises);
}
findConfigFile([
path.join(process.cwd(), 'config.json'),
path.join(process.cwd(), 'config.default.json'),
'/etc/app/config.json'
])
.then(configPath => console.log('Using config:', configPath))
.catch(error => console.error('No config found:', error.errors));
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn