可以用 Promise.race() 配合 setTimeout 实现超时控制,核心是让业务 Promise 与超时 Promise 赛跑;封装 timeout 函数返回带超时机制的新 Promise,超时则 reject 自定义错误;现代 fetch 推荐用 AbortController 精准中断请求,不支持时可用 race 方案;还可封装为 withTimeout 装饰器复用超时逻辑。

可以用 Promise.race() 配合 setTimeout 快速实现超时控制,核心是让“业务 Promise”和“超时 Promise”赛跑,谁先完成就用谁的结果。
封装一个通用的 timeout 函数
这个函数接收原始异步操作和超时毫秒数,返回一个带超时机制的新 Promise:
- 内部用
Promise.race同时运行原始操作和一个延时 reject 的 Promise - 超时 Promise 在指定时间后调用
reject,附带自定义错误(如new Error('Timeout')) - 如果原始操作先完成,就正常 resolve;若超时先触发,则整个 Promise 被 reject
使用示例:包装 fetch 请求
比如限制网络请求在 3 秒内完成:
async function fetchWithTimeout(url, timeout = 3000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const res = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return res;
} catch (err) {
clearTimeout(timeoutId);
if (err.name === 'AbortError') throw new Error('Request timeout');
throw err;
}
}
注意:现代 fetch 支持 AbortSignal,比纯 Promise.race 更精准(能真正中断请求),推荐优先使用。
兼容老环境的 race 方案
若不支持 AbortController,可用 Promise.race 手动封装:
function timeout(promise, ms) {
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeoutPromise]);
}
// 用法
try {
const data = await timeout(fetch('/api/data'), 5000);
console.log(await data.json());
} catch (err) {
console.error(err.message); // "Timeout" 或其他错误
}
⚠️ 注意:Promise.race 不会取消原始操作,只是忽略其后续结果。对资源敏感的操作(如大文件上传),建议结合 AbortController。
封装成可复用的装饰器函数
把超时逻辑抽成高阶函数,方便复用:
const withTimeout = (fn, ms) => async (...args) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ms);
try {
const result = await fn(...args, { signal: controller.signal });
clearTimeout(timeoutId);
return result;
} catch (err) {
clearTimeout(timeoutId);
throw err.name === 'AbortError'
? new Error(`${fn.name} timed out`)
: err;
}
};
// 使用
const safeFetch = withTimeout(fetch, 2000);
const res = await safeFetch('/data');
这样既保持原函数签名,又统一注入超时能力,适合批量处理多个异步函数。

















