可用 Promise.race() 配合延迟 reject 的 Promise 实现超时中断,但需结合 AbortController 才能真正取消 fetch 请求,并通过自定义 TimeoutError 区分错误类型。

可以用 Promise.race() 配合一个延迟 reject 的 Promise 来实现带超时中断的异步请求包装。核心思路是:让目标请求和一个“超时拒绝” Promise 同时竞速,谁先完成就采用谁的结果。
基础封装:通用 timeout 包装函数
写一个可复用的 timeout 函数,接收原始 Promise 和超时毫秒数:
function timeout(promise, ms) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Request timed out after ${ms}ms`)), ms);
});
return Promise.race([promise, timeoutPromise]);
}
使用示例(包装 fetch):
timeout(fetch('/api/data'), 5000)
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error('失败原因:', err.message)); // 超时或网络错误都会进这里
注意:超时不会自动取消底层请求
JavaScript 的 Promise 本身不可取消,Promise.race 只是忽略慢的那个结果,但原请求(如 fetch)仍在后台运行。若需真正中止,需结合可取消机制:
立即学习“Java免费学习笔记(深入)”;
- Fetch API 支持
AbortController,推荐搭配使用:
function timeoutFetch(url, options = {}, ms = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ms);
return fetch(url, {
...options,
signal: controller.signal
}).finally(() => clearTimeout(timeoutId));
}
这样既能响应超时,又能真正终止网络请求,避免资源浪费。
进阶:支持自定义超时错误类型与重试
实际项目中常需区分超时和其他错误,并可能配合重试逻辑:
- 抛出带标识的错误,便于
catch中判断 - 用
async/await写更清晰的流程控制
class TimeoutError extends Error {
constructor(message) {
super(message);
this.name = 'TimeoutError';
}
}
async function fetchWithTimeout(url, ms = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ms);
try {
const res = await fetch(url, { signal: controller.signal });
return await res.json();
} catch (err) {
if (err.name === 'AbortError') {
throw new TimeoutError(`Request timed out after ${ms}ms`);
}
throw err;
} finally {
clearTimeout(timeoutId);
}
}
小结:关键点
实现超时中断要注意三点:
-
竞速机制:用
Promise.race让请求和超时逻辑竞争 -
真实取消:对 fetch 等支持 signal 的 API,务必传入
AbortSignal实现物理中断 - 错误分类:区分超时错误和业务/网络错误,方便上层做差异化处理


















