乐闻世界logo
搜索文章和话题

What is the purpose of Promise.any()?

2月22日 14:07

Promise.any() is a Promise static method introduced in ES2021 that accepts an array of Promises and returns the result of the first successfully completed Promise. If all Promises fail, it returns an AggregateError.

Basic Concept

Promise.any() accepts an iterable of Promise objects as a parameter and returns a new Promise. This new Promise will complete immediately when the first Promise successfully completes, returning that Promise's result. If all Promises fail, it returns an AggregateError containing all failure reasons.

Basic Usage

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.reject('Error 2'); const promise3 = Promise.resolve('Success'); Promise.any([promise1, promise2, promise3]) .then(result => { console.log(result); // Output: Success }) .catch(error => { console.error(error); });

Comparison with Other Methods

Promise.any() vs Promise.race()

Promise.any(): Returns the first successful Promise

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.resolve('Success'); const promise3 = Promise.resolve('Another success'); Promise.any([promise1, promise2, promise3]) .then(result => console.log(result)); // Output: Success

Promise.race(): Returns the first completed Promise (whether successful or failed)

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.resolve('Success'); const promise3 = Promise.resolve('Another success'); Promise.race([promise1, promise2, promise3]) .then(result => console.log(result)) .catch(error => console.error(error)); // Output: Error 1

Promise.any() vs Promise.all()

Promise.any(): Returns as long as one succeeds

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.resolve('Success'); const promise3 = Promise.reject('Error 3'); Promise.any([promise1, promise2, promise3]) .then(result => console.log(result)); // Output: Success

Promise.all(): Must all succeed to return

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.resolve('Success'); const promise3 = Promise.reject('Error 3'); Promise.all([promise1, promise2, promise3]) .then(result => console.log(result)) .catch(error => console.error(error)); // Output: Error 1

Promise.any() vs Promise.allSettled()

Promise.any(): Returns the first successful result

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.resolve('Success'); const promise3 = Promise.reject('Error 3'); Promise.any([promise1, promise2, promise3]) .then(result => console.log(result)); // Output: Success

Promise.allSettled(): Returns status of all Promises

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.resolve('Success'); const promise3 = Promise.reject('Error 3'); Promise.allSettled([promise1, promise2, promise3]) .then(results => console.log(results)); // Output: // [ // { status: 'rejected', reason: 'Error 1' }, // { status: 'fulfilled', value: 'Success' }, // { status: 'rejected', reason: 'Error 3' } // ]

AggregateError

When all Promises fail, Promise.any() returns an AggregateError containing all failure reasons.

Basic Usage

javascript
const promise1 = Promise.reject('Error 1'); const promise2 = Promise.reject('Error 2'); const promise3 = Promise.reject('Error 3'); Promise.any([promise1, promise2, promise3]) .catch(error => { console.error(error instanceof AggregateError); // true console.error(error.message); // All promises were rejected console.error(error.errors); // ['Error 1', 'Error 2', 'Error 3'] });

Handling AggregateError

javascript
async function fetchFromMultipleSources(urls) { try { const response = await Promise.any( urls.map(url => fetch(url)) ); return await response.json(); } catch (error) { if (error instanceof AggregateError) { console.error('All data sources failed:'); error.errors.forEach((err, index) => { console.error(` ${urls[index]}: ${err.message}`); }); throw new Error('Cannot fetch data from any source'); } throw error; } }

Practical Use Cases

1. Fetch Data from Multiple Sources, Use Fastest Successful One

javascript
async function fetchFromFastestSource(sources) { try { const response = await Promise.any( sources.map(source => fetch(source.url)) ); return await response.json(); } catch (error) { if (error instanceof AggregateError) { console.error('All data sources failed'); throw new Error('Cannot fetch data'); } throw error; } } // Usage example const sources = [ { name: 'Primary server', url: 'https://api1.example.com/data' }, { name: 'Backup server 1', url: 'https://api2.example.com/data' }, { name: 'Backup server 2', url: 'https://api3.example.com/data' } ]; fetchFromFastestSource(sources) .then(data => console.log('Data:', data)) .catch(error => console.error(error));

2. Implement Timeout Mechanism

javascript
function fetchWithTimeout(url, timeout = 5000) { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Timeout')), timeout); }); return Promise.any([ fetch(url).then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }), timeoutPromise ]); } // Usage example fetchWithTimeout('/api/data', 3000) .then(data => console.log('Data:', data)) .catch(error => { if (error.message === 'Timeout') { console.error('Request timeout'); } else { console.error('Request failed:', error); } });

3. Try Multiple Backup Strategies

javascript
async function tryMultipleStrategies(strategies) { try { return await Promise.any( strategies.map(strategy => strategy()) ); } catch (error) { if (error instanceof AggregateError) { console.error('All strategies failed'); throw new Error('Cannot complete task'); } throw error; } } // Usage example const strategies = [ () => fetch('/api/v1/data').then(r => r.json()), () => fetch('/api/v2/data').then(r => r.json()), () => fetch('/api/v3/data').then(r => r.json()) ]; tryMultipleStrategies(strategies) .then(data => console.log('Data:', data)) .catch(error => console.error(error));

4. Image Loading, Use First Successfully Loaded One

javascript
async function loadFirstSuccessfulImage(imageUrls) { try { const imagePromises = imageUrls.map(url => { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = () => reject(new Error(`Failed to load: ${url}`)); img.src = url; }); }); return await Promise.any(imagePromises); } catch (error) { if (error instanceof AggregateError) { console.error('All images failed to load'); throw new Error('Cannot load image'); } throw error; } } // Usage example const imageUrls = [ 'https://cdn1.example.com/image.jpg', 'https://cdn2.example.com/image.jpg', 'https://cdn3.example.com/image.jpg' ]; loadFirstSuccessfulImage(imageUrls) .then(img => document.body.appendChild(img)) .catch(error => console.error(error));

Error Handling

Handle All Failures

javascript
async function fetchWithFallback(urls) { try { const response = await Promise.any( urls.map(url => fetch(url)) ); return await response.json(); } catch (error) { if (error instanceof AggregateError) { console.error('All URLs failed'); // Can return default value or rethrow error return { error: 'All sources failed' }; } throw error; } }

Log All Failure Reasons

javascript
async function fetchWithLogging(urls) { try { const response = await Promise.any( urls.map(url => fetch(url)) ); return await response.json(); } catch (error) { if (error instanceof AggregateError) { console.error('All URLs failed:'); error.errors.forEach((err, index) => { console.error(` ${urls[index]}: ${err.message}`); }); throw new Error('Cannot fetch data'); } throw error; } }

Performance Considerations

Empty Array Handling

javascript
Promise.any([]) .then(result => console.log(result)) .catch(error => { console.error(error instanceof AggregateError); // true console.error(error.errors); // [] });

Non-Promise Value Handling

javascript
Promise.any([1, 2, Promise.resolve(3)]) .then(result => console.log(result)); // Output: 1

Browser Compatibility

Promise.any() was introduced in ES2021 and is supported by modern browsers:

  • Chrome: 85+
  • Firefox: 79+
  • Safari: 14+
  • Edge: 85+

For older browsers, you can use a polyfill:

javascript
if (!Promise.any) { Promise.any = function(promises) { return new Promise((resolve, reject) => { const errors = []; let rejectedCount = 0; promises.forEach((promise, index) => { Promise.resolve(promise).then( value => resolve(value), error => { errors[index] = error; rejectedCount++; if (rejectedCount === promises.length) { reject(new AggregateError(errors, 'All promises were rejected')); } } ); }); if (promises.length === 0) { reject(new AggregateError(errors, 'All promises were rejected')); } }); }; }

Best Practices

1. Always Handle AggregateError

javascript
async function fetchData(sources) { try { return await Promise.any(sources.map(s => fetch(s))); } catch (error) { if (error instanceof AggregateError) { console.error('All data sources failed'); throw new Error('Cannot fetch data'); } throw error; } }

2. Provide Fallback

javascript
async function fetchWithFallback(sources, fallbackData) { try { const response = await Promise.any(sources.map(s => fetch(s))); return await response.json(); } catch (error) { if (error instanceof AggregateError) { console.warn('All data sources failed, using fallback data'); return fallbackData; } throw error; } }

3. Log Failure Reasons

javascript
async function fetchWithLogging(sources) { try { return await Promise.any(sources.map(s => fetch(s))); } catch (error) { if (error instanceof AggregateError) { error.errors.forEach((err, index) => { console.error(`${sources[index]} failed:`, err.message); }); throw new Error('Cannot fetch data'); } throw error; } }

Summary

  1. Returns first successful Promise: Returns immediately as long as one succeeds
  2. AggregateError handles all failures: Returns AggregateError when all Promises fail
  3. Suitable for multi-source scenarios: Fetch data from multiple sources, use fastest successful one
  4. Different from Promise.race(): Only returns successful results, not failed ones
  5. Different from Promise.all(): Doesn't need all to succeed, just one success is enough
  6. Provide fallback: Can provide fallback data when all fail
  7. Log failure reasons: AggregateError contains all failure reasons
标签:Promise