promise.race 实现请求超时的核心是并行执行真实请求与定时 reject 的 promise,谁先完成即生效;需用 reject 而非 resolve 确保超时触发失败,推荐结合 abortcontroller 真正中止请求。

用 Promise.race 给异步请求加超时,核心思路是:把「真实请求」和「定时拒绝的 Promise」一起传入 race,谁先完成(fulfilled 或 rejected),就以它的结果为准。一旦超时 Promise 先触发 reject,整个 race 就立刻失败,从而中断等待。
构造一个可拒绝的超时 Promise
超时不能只是“等一会儿”,必须是一个会在指定时间后 主动 reject 的 Promise。常见写法:
function timeout(ms, message = 'Request timed out') {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error(message)), ms);
});
}
注意:这里用 reject 而不是 resolve,因为我们要让 race 在超时时“失败退出”,而不是返回一个无意义的值。
与 fetch 或其他 Promise 请求组合使用
把真实请求(如 fetch)和 timeout() 一起丢进 Promise.race:
async function fetchWithTimeout(url, timeoutMs = 5000) {
try {
const res = await Promise.race([
fetch(url),
timeout(timeoutMs)
]);
return res;
} catch (err) {
// err 可能是网络错误,也可能是超时错误
if (err.message === 'Request timed out') {
console.warn('请求超时,已中止');
}
throw err;
}
}
⚠️ 注意:fetch 本身不会因网络卡顿自动 reject,它只在根本连不上、DNS 失败等情况下 reject;而超时 Promise 确保了“最多等 N 毫秒”,这是对用户感知层面的控制。
更健壮的封装:支持 AbortController(推荐搭配使用)
Promise.race 能“中断等待”,但不能真正取消底层请求(比如 fetch 还在后台发包)。现代浏览器中,应优先用 AbortController 主动中止:
- 创建
const controller = new AbortController() - 把
controller.signal传给fetch({ signal }) - 在超时回调里调用
controller.abort() - 再用
race协调逻辑流,保持语义清晰
async function fetchWithAbort(url, timeoutMs = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await Promise.race([
fetch(url, { signal: controller.signal }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timed out')), timeoutMs)
)
]);
clearTimeout(timeoutId);
return res;
} catch (err) {
clearTimeout(timeoutId);
throw err;
}
}
注意事项与常见坑
-
Promise.race不会取消已发起的 Promise,只是不等它——所以搭配AbortController才算真正“中断” - 超时 Promise 的 reject 值要容易识别(比如带特定 message),方便区分是超时还是业务错误
- 不要在
race里放多个可能 resolve 的 Promise,否则无法保证哪个逻辑路径生效 - 如果请求本身返回的是
Promise.resolve(undefined),而超时 Promise 更快,你拿到的就是 reject,不会意外走 success 分支
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











