用 promise.race 实现超时熔断,结合 abortcontroller 可真正中止请求;需 cleartimeout 避免内存泄漏,并区分 aborterror 统一处理超时错误。

用 Promise 实现带超时熔断的异步请求,核心是同时启动目标请求和一个定时器,谁先完成谁“胜出”,另一个被忽略或取消(如果支持取消)。
基础思路:Promise.race + setTimeout
最常用、兼容性最好的方式是用 Promise.race 让「真实请求」和「超时拒绝」竞争:
- 创建一个在指定毫秒后 reject 的 Promise(即超时逻辑)
- 用 Promise.race 包裹「请求 Promise」和「超时 Promise」
- 谁先 settle(fulfill 或 reject),race 就立即以它的结果结束
示例代码:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
return Promise.race([
fetch(url, { signal: controller.signal })
.finally(() => clearTimeout(timeoutId)),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timed out')), timeout)
)
]);
}
推荐写法:结合 AbortController(更健壮)
单纯靠 race 只能“忽略”慢请求,但 fetch 还在后台运行,可能浪费资源。现代浏览器支持 AbortController,可真正中止请求:
- 创建 AbortController 实例,传入
signal给 fetch - 用 setTimeout 调用
controller.abort()触发中止 - fetch 抛出
AbortError,你可在 catch 中统一处理超时 - 记得在 finally 清理定时器,避免内存泄漏
封装成可复用工具函数
把逻辑抽出来,支持自定义错误、返回结构化响应:
function fetchWithTimeout(url, options = {}, timeout = 5000) {const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
return fetch(url, {
...options,
signal: controller.signal
})
.then(res => {
clearTimeout(timeoutId);
return res;
})
.catch(err => {
clearTimeout(timeoutId);
if (err.name === 'AbortError') {
throw new Error(`Request to ${url} timed out after ${timeout}ms`);
}
throw err;
});
}
注意点与边界情况
- fetch 不支持取消时(如旧版 Safari):AbortController 无效,仍需依赖 race + 手动 reject,但请求会继续执行
- 超时时间设太短:可能误伤正常慢网用户,建议根据接口 SLA 设定,比如 8s 内未响应视为失败
- 重复调用未清理定时器:务必在 then/catch/finally 中 clearTimeout,否则可能触发已废弃的 reject
- 需要重试逻辑?:超时熔断 ≠ 重试;若需自动重试,应在外层包装 retry 逻辑,而非塞进 timeout 函数里
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











