本篇依然来自于我们的 《前端周刊》 项目! 由团队成员 嘿嘿 翻译,他的文章风格稳健而清晰,注重结构与逻辑的严谨性,善于用简洁的语言将复杂技术拆解成易于理解的知识点~ 欢迎大家他探讨,持续追踪全球前端领域的最新动态! 原文:Understanding Promise.any() - when one success is enough
在 JavaScript 中处理异步代码时,Promise 一直是我们的老朋友。如果你用过 Promise.all() 或 Promise.race() 来协调异步操作,那你对这些模式不会陌生。但如果你只对第一个成功的结果感兴趣,并且想要忽略失败呢?这就是 Promise.any() 登场的时机了:它会以第一个成功的 Promise 作为结果,并忽略所有失败的情况(除非全部失败)。
Promise.any(iterable)[AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError)。一个空的可迭代对象会立即以 AggregateError 拒绝,并且其 .errors 数组为空。
想象一下,你要查询多个第三方接口,你只关心第一个能成功返回结果的接口:
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,只要有一个成功就能工作:
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 失败了。AggregateError,其中包含 .errors 数组记录所有失败原因。AbortController。Promise.any() 时,Promise 已经开始执行,它不会延迟执行。.errors 数组的 AggregateError 拒绝。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']
});方法 | 何时 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.all()。Promise.race()。Promise.allSettled() 或手动 map() + .catch()。Promise.any() 是一个现代且优雅的解决方案,适用于只要一次成功就够了、并且失败是预期中的场景。无论是优化 API 请求,还是提升用户体验,它都是异步工具箱中的一大利器。
在下一个异步流程里试试它吧,尤其是当失败很可能发生时,让你的成功来自第一个抵达的来源。