用闭包封装可配置超时的请求函数:基于 fetch + AbortController,timeout 动态传入;支持链式配置的工厂函数更实用;兼容 IE 时可用 XMLHttpRequest.timeout;需 clearTimeout 防泄漏、校验 timeout 类型。

用闭包封装一个可配置超时时间的请求函数,核心是把 timeout 值作为参数传入,而不是硬编码在内部。
用 fetch 封装支持动态 timeout 的函数
直接基于 fetch + AbortController 实现,timeout 可每次调用时传入:
- 创建
AbortController实例 - 用
setTimeout在指定毫秒后调用controller.abort() - 将
controller.signal传给fetch的signal选项 - 捕获
AbortError并统一抛出超时错误
示例代码:
function request(url, options = {}) {
return (timeout = 5000) => {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
return fetch(url, {
...options,
signal: controller.signal
})
.then(res => {
clearTimeout(id);
return res;
})
.catch(err => {
clearTimeout(id);
if (err.name === 'AbortError') {
throw new Error(`Request timed out after ${timeout}ms`);
}
throw err;
});
};
}
// 使用:可灵活指定超时时间
const getUser = request('/api/user', { method: 'GET' });
getUser(3000).then(r => r.json()).catch(console.error); // 3s 超时
getUser(10000).then(r => r.json()).catch(console.error); // 10s 超时
支持链式调用与默认 timeout 的工厂函数
更实用的方式是返回一个“请求构造器”,先设默认值,再通过方法动态覆盖:
立即学习“Java免费学习笔记(深入)”;
- 返回对象含
timeout(ms)方法,返回新函数 - 最终调用
send()发起请求 - 所有配置(url、method、headers、timeout)都可运行时决定
示例:
function createRequest(url, options = {}) {
let timeout = 5000;
return {
timeout(ms) {
timeout = ms;
return this;
},
send() {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
return fetch(url, {
...options,
signal: controller.signal
})
.then(res => {
clearTimeout(id);
return res;
})
.catch(err => {
clearTimeout(id);
if (err.name === 'AbortError') {
throw new Error(`Timeout: ${timeout}ms`);
}
throw err;
});
}
};
}
// 使用
createRequest('/api/data')
.timeout(8000)
.send()
.then(r => r.json());
兼容 XMLHttpRequest 的写法(需支持老环境)
若需兼容不支持 AbortController 的浏览器(如 IE),可用 XMLHttpRequest 手动控制:
- 调用
xhr.timeout = ms设置原生超时(注意:仅对 open 后生效) - 监听
xhr.ontimeout处理超时逻辑 - 注意
xhr.timeout不触发onerror,需单独处理
简版示例:
function xhrRequest(url, options = {}) {
return (timeout = 5000) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(options.method || 'GET', url);
xhr.timeout = timeout;
xhr.onload = () => resolve(xhr);
xhr.onerror = () => reject(new Error('Network error'));
xhr.ontimeout = () => reject(new Error(`Timeout after ${timeout}ms`));
if (options.headers) {
Object.entries(options.headers).forEach(([k, v]) => xhr.setRequestHeader(k, v));
}
xhr.send(options.body || null);
});
};
}
小贴士:实际使用中的关键细节
避免踩坑,注意这些点:
- 务必 clearTimeout:Promise 成功或失败后都要清除定时器,防止内存泄漏
- timeout 应为数字:传入非数字(如 undefined)会导致立即 abort,建议做类型校验
- 服务端超时 ≠ 客户端超时:客户端 timeout 只控制请求发起后的等待时间,不包含 DNS、TCP 握手等前置耗时(fetch 中这部分也受 signal 控制)
- 重试逻辑可叠加:可在上述函数外再包一层 retry,配合动态 timeout 实现退避重试


















