Promise.race()可实现竞速请求与超时控制:将fetch Promise与超时reject Promise传入race,谁先settled即采用其结果;需配合AbortController中断连接,并在finally清理定时器。

Promise.race() 可以用来实现竞速请求 + 超时控制,核心思路是:把业务请求 Promise 和一个“超时拒绝”的 Promise 一起传入 race,谁先完成(fulfilled 或 rejected),就采用它的结果。
用 Promise.race 实现带超时的 fetch 请求
最常见场景是给网络请求加超时限制。比如调用 API,超过 5 秒就中断并报错:
- 构造一个正常 fetch 的 Promise
- 同时构造一个 5 秒后 reject 的 Promise(用 setTimeout 包装)
- 用 Promise.race 包裹这两个 Promise
- 如果 fetch 先返回,整个 race 就 fulfilled;如果超时先触发,整个 race 就 rejected
示例代码:
function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const fetchPromise = fetch(url, { signal: controller.signal });
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('请求超时')), timeout);
});
return Promise.race([fetchPromise, timeoutPromise])
.finally(() => clearTimeout(timeoutId));
}
注意 abort 与 timeout 的协同处理
单纯靠 race 无法自动取消正在进行的 fetch,所以推荐配合 AbortController 使用:
立即学习“Java免费学习笔记(深入)”;
- fetch 的 signal 用于真正中断网络连接(避免资源浪费)
- timeoutPromise 仅负责在超时时触发 reject,让 race 结束
- 两者结合才能既及时响应超时,又释放底层连接
- 务必在 finally 中清理 setTimeout,防止内存泄漏
race 不等于“只取成功结果”
Promise.race 触发条件是任一 Promise settled(fulfilled 或 rejected),不是只等 fulfilled:
- 如果超时 Promise 先 reject,整个 race 就立即 reject,不会等 fetch
- 如果 fetch 返回 404/500,它本身是 fulfilled(因为 fetch 不会因 HTTP 状态码 reject),但你可以后续用 response.ok 判断再手动 throw
- 若需区分超时和其他错误,可在 catch 中检查 error.message 是否包含“超时”
替代方案:用 Promise.any 或封装 retry
race 适合“只要一个结果”,但不适用于需要“多个请求中取最快的成功响应”这种场景:
- 比如同时请求 CDN 和备用源,哪个先成功就用哪个 → 还是用 race,但所有 Promise 都要 catch 错误转为 resolve(null),再过滤 null
- 如果想“等至少一个成功”,用 Promise.any(ES2021+),它忽略 rejected,只等第一个 fulfilled
- 超时 + 重试组合?建议封装成独立函数,内部用 race 控制单次超时,外层循环重试逻辑


















