用 promise.race 实现异步超时的核心是让原 promise 与定时 reject 的 promise 竞争,先完成者决定结果;需注意 promise 本身不可取消,真正终止任务需结合 abortcontroller 等机制。

用 Promise.race 实现异步超时,核心思路是:把目标异步操作和一个「定时拒绝」的 Promise 一起传入 race,谁先完成就取谁的结果——如果目标操作太慢,超时 Promise 先 reject,就能及时中断等待。
构造一个带超时的 Promise 包装器
这是最常用也最实用的方式。写一个通用函数,接收原始 Promise 和超时毫秒数,返回带超时控制的新 Promise:
- 用
Promise.race([original, timeoutPromise])让两者竞争 - 超时 Promise 用
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms))实现 - 注意:原 Promise 不会自动取消(JS 中 Promise 本身不可取消),但 race 返回结果后,你可以忽略后续回调或在业务层做清理
示例:
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Operation timed out after ${ms}ms`)), ms);
});
return Promise.race([promise, timeout]);
}
// 使用
withTimeout(fetch('/api/data'), 3000)
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error('出错了:', err.message)); // 可能是网络错误,也可能是超时错误
区分超时错误和其他错误
实际项目中,你往往需要单独处理超时逻辑(比如重试、降级、提示用户),而不是把超时和请求失败混为一谈。可以自定义错误类型或检查 error message:
- 给超时错误加特定标识,比如
reject(Object.assign(new Error(...), { code: 'TIMEOUT' })) - 或者用 instanceof + 自定义类(更健壮)
- 在 catch 中用
err.code === 'TIMEOUT'或err.message.includes('timed out')判断
注意:Promise.race 不会取消正在运行的任务
这是关键限制,必须明确:
-
fetch、setTimeout、async/await中的 await 等底层操作,一旦启动就不会因为 race 结束而停止 - 只是你的代码不再等待它,但浏览器可能还在发请求、定时器仍在计时、资源未释放
- 如需真正取消(比如终止 fetch 请求),得配合
AbortController等机制,再手动调用abort()
结合 AbortController 做真取消(推荐进阶用法)
现代浏览器中,fetch 支持 signal,可与超时联动:
function fetchWithTimeout(url, ms) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ms);
return fetch(url, { signal: controller.signal })
.finally(() => clearTimeout(timeoutId));
}
// 使用
fetchWithTimeout('/api/data', 3000)
.then(res => res.json())
.catch(err => {
if (err.name === 'AbortError') {
console.error('请求被主动取消(超时)');
} else {
console.error('其他错误:', err);
}
});
这种方式既实现了超时感知,又真正终止了网络请求,比纯 Promise.race 更干净可靠。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











