javascript无原生拦截器,但可通过axios拦截器或fetch+高阶函数封装实现,核心是请求/响应前后期处理及基于错误类型、幂等性、退避策略的可控重试。

JavaScript 中没有原生的“拦截器(Interceptor)”类型,但可以通过函数式组合、类封装或请求库(如 Axios)的拦截机制来实现类似效果。要封装一个带自动重试能力的请求库,核心是:在发起请求前/后插入逻辑(如加 token、日志、错误处理),并在失败时按策略重新发起请求。
用 Axios 实现拦截器 + 自动重试
Axios 原生支持请求/响应拦截器,并可通过 cancel token 或 promise 链控制重试。关键点在于:避免无限重试、区分可重试错误、控制间隔时间。
- 使用 request interceptor 统一添加 headers、鉴权信息
- 使用 response interceptor 捕获错误,对网络失败、5xx、部分 4xx(如 408、429)触发重试
- 重试逻辑不写在拦截器内(易导致递归),而是封装为独立函数,在响应拦截中调用
- 每次重试应带退避(如指数退避),并限制最大次数(如 3 次)
手动封装轻量请求函数(无第三方依赖)
若不想引入 Axios,可用 fetch + Promise 封装。通过高阶函数注入“拦截行为”,再用递归或循环实现重试。
- 定义 interceptRequest:接收原始配置,返回增强后的配置(如自动加 Authorization)
- 定义 interceptResponse:接收 response 或 error,决定是否重试或抛出业务错误
- 主函数
retryFetch(url, options, { maxRetries = 2, delay = 1000 })内部调用拦截函数,并用setTimeout或Promise.delay控制重试节奏 - 注意:fetch 不会因 HTTP 状态码(如 404、500)reject,需手动
if (!res.ok) throw new Error(...)
重试策略的关键细节
盲目重试可能加重服务压力或掩盖真实问题,需有判断依据。
-
只重试幂等请求:默认仅对
GET、HEAD、OPTIONS重试;POST等非幂等方法需显式开启(如传idempotent: true) - 跳过特定错误:400(参数错误)、401(需登录)、403(权限不足)通常不应重试;而网络中断、502、504、超时更适合重试
-
记录重试过程:在拦截器中打日志(如
Retry #1 for /api/data...),便于排查 - 取消重复请求:配合 AbortController,当新请求发出时取消上一个未完成的请求(尤其适用于搜索建议等场景)
一个可直接用的最小示例(基于 fetch)
以下是一个简明、可扩展的封装:
const createApiClient = (baseURL = '') => {
const defaultOptions = { maxRetries: 2, retryDelay: 1000 };
<p>const withAuth = (options) => ({
...options,
headers: {
'Authorization': <code>Bearer ${localStorage.getItem('token') || ''}</code>,
...options.headers
}
});</p><p>const shouldRetry = (error, attempt) => {
if (attempt > defaultOptions.maxRetries) return false;
if (error.name === 'AbortError') return false;
if (error.message.includes('failed to fetch')) return true; // 网络异常
if (error.responseStatus && [502, 503, 504].includes(error.responseStatus)) return true;
return false;
};</p><p>const request = async (url, options = {}) => {
const config = { ...defaultOptions, ...options };
const finalURL = baseURL + url;
let lastError;</p><pre class="brush:php;toolbar:false;">for (let i = 0; i setTimeout(r, config.retryDelay * Math.pow(2, i)));
}
}
throw lastError;};
return { get: (u, o) => request(u, { method: 'GET', ...o }) }; };
// 使用 const api = createApiClient('/api'); api.get('/users').then(console.log).catch(console.error);











