本篇依然来自于我们的 《前端周刊》 项目!
由团队成员 嘿嘿 翻译,他的文章风格稳健而清晰,注重结构与逻辑的严谨性,善于用简洁的语言将复杂技术拆解成易于理解的知识点~
欢迎大家 进群 与他探讨,持续追踪全球前端领域的最新动态!
在 JavaScript 中处理异步代码时,Promise 一直是我们的老朋友。如果你用过 Promise.all()
或 Promise.race()
来协调异步操作,那你对这些模式不会陌生。但如果你只对第一个成功的结果 感兴趣,并且想要忽略失败呢?这就是 Promise.any()
登场的时机了:它会以第一个成功的 Promise 作为结果 ,并忽略所有失败的情况(除非全部失败)。
它是如何工作的
javascript
Promise.any(iterable)
- 它接收一个 Promise 可迭代对象。
- 一旦有一个成功就立即返回。
- 如果全部 失败,会抛出一个
[AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError)
。
一个空的可迭代对象 会立即以 AggregateError
拒绝,并且其 .errors
数组为空。
日常使用场景
API 兜底(Fallback APIs)
想象一下,你要查询多个第三方接口,你只关心第一个能成功返回结果的接口:
javascript
const fetchWithCheck = (url) =>
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP 错误: ${response.status}`);
}
return response;
});
Promise.any([
fetchWithCheck('https://mirror1.example.com/data'),
fetchWithCheck('https://mirror2.example.com/data'),
fetchWithCheck('https://mirror3.example.com/data')
])
.then(response => response.json())
.then(data => {
console.log('从第一个可用的服务器拿到数据:', data);
})
.catch(error => {
if (error instanceof AggregateError) {
console.error('所有服务器都失败了:', error.errors);
} else {
console.error('意外错误:', error);
}
});
⚠️ 注意: fetch()
只会在网络错误(例如离线)或 CORS 策略限制时才会 reject,这些情况表现为通用的网络失败。
你也可以构建一个功能,尝试多个浏览器 API,只要有一个成功就能工作:
javascript
const tryClipboardAPI = () =>
navigator.clipboard.readText().then(text => `剪贴板: ${text}`);
const tryLegacyPrompt = () =>
new Promise((resolve, reject) => {
const text = prompt('请粘贴你的数据:');
if (text === null) {
reject(new Error('用户取消了输入'));
} else {
resolve(`输入框: ${text}`);
}
});
Promise.any([
tryClipboardAPI(),
tryLegacyPrompt()
])
.then(result => console.log('拿到用户输入:', result))
.catch(error => {
if (error instanceof AggregateError) {
console.error('没有可用的输入方式:', error.errors);
} else {
console.error('意外错误:', error);
}
});
这种模式能让功能更灵活、更友好,即使是在缺少现代 API 的老浏览器里也能用。
需要注意的坑点
Promise.any()
会以第一个成功的结果解决,并忽略其他的,即使后续的 Promise 失败了。- 如果所有 Promise 都失败 ,会抛出
AggregateError
,其中包含.errors
数组记录所有失败原因。 - 它不会中止 其他未完成的 Promise。如果需要取消,可以使用
AbortController
。 - 在调用
Promise.any()
时,Promise 已经开始执行,它不会延迟执行。 - 一个空的可迭代对象 会立即以带空
.errors
数组的AggregateError
拒绝。
示例:处理 AggregateError
javascript
Promise.any([
Promise.reject(new Error('错误 A')),
Promise.reject(new Error('错误 B'))
])
.catch(err => {
console.log(err instanceof AggregateError); // true
console.log(err.errors.map(e => e.message)); // ['错误 A', '错误 B']
});
与其他 Promise 方法对比
|方法|何时 resolve...|何时 reject...|说明| |-|-|-|-| |Promise.all()
|✅ 全部成功时|❌ 任意失败时|适合需要全部结果 时使用。| |Promise.any()
|🟢 第一个成功时|🔴 全部失败时|适合只要有一个成功就够了 。| |Promise.race()
|🏁 第一个完成时(无论成功或失败)|⚠️ 同左|返回第一个完成 的结果。| |Promise.allSettled()
|📦 全部完成时|🚫 永远不会 reject|返回所有结果的完整状态。|
浏览器支持情况
Promise.any()
在所有现代浏览器(Chrome 85+、Firefox 79+、Safari 14+、Edge 85+)以及 Node.js 15+ 中都受支持。对于旧环境,可以使用 [core-js](https://www.npmjs.com/package/core-js)
等 polyfill。
什么时候不该用 Promise.any()
当你只想要至少一次成功 ,并且允许部分失败时,可以使用 Promise.any()
。但请避免以下场景使用:
- 你需要所有结果 → 用
Promise.all()
。 - 你想要第一个完成的结果 ,无论成功还是失败 → 用
Promise.race()
。 - 你需要分别处理每个结果 → 用
Promise.allSettled()
或手动map()
+.catch()
。
成功优先的异步实践
Promise.any()
是一个现代且优雅的解决方案,适用于只要一次成功就够了 、并且失败是预期中的场景。无论是优化 API 请求,还是提升用户体验,它都是异步工具箱中的一大利器。
在下一个异步流程里试试它吧,尤其是当失败很可能发生时,让你的成功来自第一个抵达的来源。