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
javascriptconst 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
javascriptconst 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)
javascriptconst 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
javascriptconst 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
javascriptconst 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
javascriptconst 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
javascriptconst 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
javascriptconst 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
javascriptasync 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
javascriptasync 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
javascriptfunction 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
javascriptasync 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
javascriptasync 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
javascriptasync 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
javascriptasync 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
javascriptPromise.any([]) .then(result => console.log(result)) .catch(error => { console.error(error instanceof AggregateError); // true console.error(error.errors); // [] });
Non-Promise Value Handling
javascriptPromise.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:
javascriptif (!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
javascriptasync 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
javascriptasync 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
javascriptasync 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
- Returns first successful Promise: Returns immediately as long as one succeeds
- AggregateError handles all failures: Returns AggregateError when all Promises fail
- Suitable for multi-source scenarios: Fetch data from multiple sources, use fastest successful one
- Different from Promise.race(): Only returns successful results, not failed ones
- Different from Promise.all(): Doesn't need all to succeed, just one success is enough
- Provide fallback: Can provide fallback data when all fail
- Log failure reasons: AggregateError contains all failure reasons