在 JavaScript 的异步编程中,Promise 一直是处理异步操作的核心机制。随着 ECMAScript 标准的不断演进,ES2021 (ES12) 引入了 Promise.any()
方法,为 Promise 处理模式增添了新的维度。本文将深入探讨 Promise.any()
的工作原理、使用场景以及它与现有 Promise 组合方法的区别。
Promise.any 的基本概念
Promise.any()
是一个静态方法,它接收一个 Promise 可迭代对象(如数组),并返回一个新的 Promise:
javascript
Promise.any(iterable);
其核心行为是"竞速成功"(short-circuits when any promise fulfills):
- 只要有一个 Promise 成功(fulfilled),就立即返回该成功值
- 只有当所有 Promise 都失败(rejected)时,才会返回一个包含所有拒绝原因的 AggregateError
与现有 Promise 方法的对比
为了更好地理解 Promise.any()
,让我们将其与其他 Promise 组合方法进行比较:
方法 | 描述 | 短路条件 |
---|---|---|
Promise.all |
所有成功或一个失败 | 第一个拒绝 |
Promise.race |
第一个敲定(无论成功或失败) | 第一个敲定 |
Promise.allSettled |
等待所有敲定,不短路 | 不短路 |
Promise.any |
第一个成功或全部失败 | 第一个成功 |
使用示例
基本用法
javascript
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));
Promise.any([promise1, promise2, promise3])
.then((value) => console.log(value)); // 输出: "quick"
全部失败的情况
javascript
const promises = [
Promise.reject('Error 1'),
Promise.reject('Error 2'),
Promise.reject('Error 3')
];
Promise.any(promises)
.catch((error) => {
console.log(error.errors); // 输出: ['Error 1', 'Error 2', 'Error 3']
});
实际应用场景
-
多源数据获取:从多个备用API获取数据,只要有一个成功即可
javascriptconst apis = [ fetch('https://api.backup1.com/data'), fetch('https://api.backup2.com/data'), fetch('https://api.backup3.com/data') ]; Promise.any(apis) .then(response => processData(response)) .catch(allErrors => logErrors(allErrors));
-
性能优化:从最快的CDN加载资源
javascriptconst cdnUrls = [ 'https://cdn1.example.com/library.js', 'https://cdn2.example.com/library.js', 'https://cdn3.example.com/library.js' ]; Promise.any(cdnUrls.map(url => fetch(url))) .then(response => loadScript(response.url));
-
冗余操作:执行多个相同的操作以确保至少一个成功
javascriptconst databaseWrites = [ writeToPrimaryDatabase(data), writeToSecondaryDatabase(data) ]; Promise.any(databaseWrites) .then(() => console.log('Data persisted successfully'));
错误处理
当所有 Promise 都拒绝时,Promise.any()
会抛出一个 AggregateError
,这是一个新的错误类型,包含了所有拒绝原因:
javascript
try {
await Promise.any(rejectedPromises);
} catch (error) {
if (error instanceof AggregateError) {
console.error('All promises failed:', error.errors);
} else {
console.error('Unexpected error:', error);
}
}
浏览器兼容性与polyfill
截至知识截止日期,Promise.any()
已在现代浏览器中实现,但对于旧环境,可以使用以下polyfill:
javascript
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"));
}
promises.forEach(promise => {
Promise.resolve(promise)
.then(resolve)
.catch(reason => {
rejections.push(reason);
pending--;
if (pending === 0) {
reject(new AggregateError(rejections, "All promises were rejected"));
}
});
});
});
};
}
总结
Promise.any()
是 ES2021 引入的一个强大工具,它填补了 JavaScript Promise API 的空白,为"竞速成功"场景提供了原生支持。通过理解其与 Promise.all()
、Promise.race()
和 Promise.allSettled()
的区别,开发者可以更精确地选择适合不同异步场景的 Promise 组合方法。
在实际应用中,Promise.any()
特别适合需要冗余执行、备用方案或优化响应时间的场景。随着 JavaScript 异步编程的不断发展,掌握这些工具将使开发者能够编写更健壮、更高效的异步代码。