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

Promise.any() 的作用是什么?

2月22日 14:07

Promise.any() 是 ES2021 引入的 Promise 静态方法,它接收一个 Promise 数组,返回第一个成功完成的 Promise 的结果。如果所有 Promise 都失败,则返回 AggregateError。

基本概念

Promise.any() 接收一个可迭代的 Promise 对象作为参数,返回一个新的 Promise。这个新的 Promise 会在第一个 Promise 成功完成时立即完成,返回该 Promise 的结果。如果所有 Promise 都失败,则返回一个 AggregateError,包含所有失败的原因。

基本用法

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.reject('错误2'); const promise3 = Promise.resolve('成功'); Promise.any([promise1, promise2, promise3]) .then(result => { console.log(result); // 输出: 成功 }) .catch(error => { console.error(error); });

与其他方法的对比

Promise.any() vs Promise.race()

Promise.any(): 返回第一个成功的 Promise

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.resolve('成功'); const promise3 = Promise.resolve('另一个成功'); Promise.any([promise1, promise2, promise3]) .then(result => console.log(result)); // 输出: 成功

Promise.race(): 返回第一个完成的 Promise(无论成功或失败)

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.resolve('成功'); const promise3 = Promise.resolve('另一个成功'); Promise.race([promise1, promise2, promise3]) .then(result => console.log(result)) .catch(error => console.error(error)); // 输出: 错误1

Promise.any() vs Promise.all()

Promise.any(): 只要有一个成功就返回

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.resolve('成功'); const promise3 = Promise.reject('错误3'); Promise.any([promise1, promise2, promise3]) .then(result => console.log(result)); // 输出: 成功

Promise.all(): 必须全部成功才返回

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.resolve('成功'); const promise3 = Promise.reject('错误3'); Promise.all([promise1, promise2, promise3]) .then(result => console.log(result)) .catch(error => console.error(error)); // 输出: 错误1

Promise.any() vs Promise.allSettled()

Promise.any(): 返回第一个成功的结果

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.resolve('成功'); const promise3 = Promise.reject('错误3'); Promise.any([promise1, promise2, promise3]) .then(result => console.log(result)); // 输出: 成功

Promise.allSettled(): 返回所有 Promise 的状态

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.resolve('成功'); const promise3 = Promise.reject('错误3'); Promise.allSettled([promise1, promise2, promise3]) .then(results => console.log(results)); // 输出: // [ // { status: 'rejected', reason: '错误1' }, // { status: 'fulfilled', value: '成功' }, // { status: 'rejected', reason: '错误3' } // ]

AggregateError

当所有 Promise 都失败时,Promise.any() 会返回一个 AggregateError,包含所有失败的原因。

基本用法

javascript
const promise1 = Promise.reject('错误1'); const promise2 = Promise.reject('错误2'); const promise3 = Promise.reject('错误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); // ['错误1', '错误2', '错误3'] });

处理 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('所有数据源都失败了:'); error.errors.forEach((err, index) => { console.error(` ${urls[index]}: ${err.message}`); }); throw new Error('无法从任何数据源获取数据'); } throw error; } }

实际应用场景

1. 从多个数据源获取数据,使用最快成功的

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('所有数据源都失败了'); throw new Error('无法获取数据'); } throw error; } } // 使用示例 const sources = [ { name: '主服务器', url: 'https://api1.example.com/data' }, { name: '备用服务器1', url: 'https://api2.example.com/data' }, { name: '备用服务器2', url: 'https://api3.example.com/data' } ]; fetchFromFastestSource(sources) .then(data => console.log('数据:', data)) .catch(error => console.error(error));

2. 实现超时机制

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 ]); } // 使用示例 fetchWithTimeout('/api/data', 3000) .then(data => console.log('数据:', data)) .catch(error => { if (error.message === 'Timeout') { console.error('请求超时'); } else { console.error('请求失败:', error); } });

3. 尝试多个备份方案

javascript
async function tryMultipleStrategies(strategies) { try { return await Promise.any( strategies.map(strategy => strategy()) ); } catch (error) { if (error instanceof AggregateError) { console.error('所有策略都失败了'); throw new Error('无法完成任务'); } throw error; } } // 使用示例 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)) .catch(error => console.error(error));

4. 图片加载,使用第一个成功加载的

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('所有图片都加载失败'); throw new Error('无法加载图片'); } throw error; } } // 使用示例 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));

错误处理

处理所有失败的情况

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('所有 URL 都失败了'); // 可以返回默认值或重新抛出错误 return { error: 'All sources failed' }; } throw error; } }

记录所有失败的原因

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('所有 URL 都失败了:'); error.errors.forEach((err, index) => { console.error(` ${urls[index]}: ${err.message}`); }); throw new Error('无法获取数据'); } throw error; } }

性能考虑

空数组处理

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

非 Promise 值处理

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

浏览器兼容性

Promise.any() 是 ES2021 引入的,现代浏览器都支持:

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

对于旧浏览器,可以使用 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')); } }); }; }

最佳实践

1. 总是处理 AggregateError

javascript
async function fetchData(sources) { try { return await Promise.any(sources.map(s => fetch(s))); } catch (error) { if (error instanceof AggregateError) { console.error('所有数据源都失败了'); throw new Error('无法获取数据'); } throw error; } }

2. 提供降级方案

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('所有数据源都失败,使用降级数据'); return fallbackData; } throw error; } }

3. 记录失败原因

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]} 失败:`, err.message); }); throw new Error('无法获取数据'); } throw error; } }

总结

  1. 返回第一个成功的 Promise:只要有一个成功就立即返回
  2. AggregateError 处理所有失败:所有 Promise 都失败时返回 AggregateError
  3. 适合多数据源场景:从多个数据源获取数据,使用最快成功的
  4. 与 Promise.race() 不同:只返回成功的结果,不返回失败的结果
  5. 与 Promise.all() 不同:不需要全部成功,只要一个成功即可
  6. 提供降级方案:所有失败时可以提供降级数据
  7. 记录失败原因:AggregateError 包含所有失败的原因
标签:Promise