可以用 promise.race 配合 settimeout 实现带超时控制的函数包装器,核心是让目标函数与延迟拒绝的 promise 赛跑;基础版本兼容同步和异步函数,增强版本支持 abortsignal 以优雅中断操作,并建议使用自定义 timeouterror 类便于精准捕获。

可以用 Promise.race 配合 setTimeout 快速实现一个带超时控制的函数包装器,核心思路是让目标函数和一个延迟拒绝的 Promise 赛跑,谁先完成就用谁的结果。
基础版本:支持 Promise 和普通函数
这个版本能处理返回 Promise 的异步函数,也能兼容同步函数(自动包装成 Promise):
function withTimeout(fn, ms) {
return function(...args) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms);
});
const result = fn(...args);
// 如果返回的是 Promise,race;否则转成 resolved Promise 再 race
return Promise.race([
Promise.resolve(result),
timeoutPromise
]);
};
}
使用示例:
const fetchWithTimeout = withTimeout(fetch, 5000);
fetchWithTimeout('https://api.example.com/data')
.then(res => res.json())
.catch(err => console.error('失败原因:', err.message));
增强版本:支持取消信号(AbortSignal)
对于原生支持 AbortSignal 的 API(如 fetch、setTimeout 封装等),可以更优雅地中断操作,避免资源浪费:
function withTimeoutAndAbort(fn, ms) {
return function(...args) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ms);
try {
const result = fn(...args, { signal: controller.signal });
clearTimeout(timeoutId);
return result;
} catch (e) {
clearTimeout(timeoutId);
throw e;
}
};
}
注意:该方式依赖目标函数是否接受并响应 signal 参数。常见如 fetch、window.setTimeout(需自行封装)、ReadableStream 等已支持。
实用建议与注意事项
- 超时错误应有明确标识(比如 error.name = 'TimeoutError'),方便上层区分处理
- 避免对已 resolve/reject 的 Promise 重复调用 then/catch,race 本身已保证只触发一次
- 若被包装函数内部有定时器或事件监听,超时后需手动清理(如 clearTimeout、removeEventListener),否则可能造成内存泄漏
- Node.js 环境中可考虑使用 AbortController(v15.4+ 原生支持)或 polyfill
小技巧:支持自定义超时错误类
便于类型判断和统一错误处理:
class TimeoutError extends Error {
constructor(ms) {
super(`Operation timed out after ${ms}ms`);
this.name = 'TimeoutError';
this.code = 'ERR_TIMEOUT';
}
}
function withTimeout(fn, ms) {
return function(...args) {
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new TimeoutError(ms)), ms)
);
return Promise.race([Promise.resolve(fn(...args)), timeoutPromise]);
};
}
这样就能用 err instanceof TimeoutError 精准捕获超时场景。











