如何在多请求触发401错误时仅执行一次Axios拦截器?
解决Axios并行401仅刷新一次Token并重试请求的问题
你的代码核心逻辑方向没问题,但缺少对后续请求的等待处理、错误边界覆盖,导致并行场景下无法正确触发重试。以下是修复后的完整实现:
let isRefreshing = false; let failedQueue = []; // 统一处理队列中等待的请求 function processQueue(token) { failedQueue.forEach(promise => { if (token) { promise.resolve(token); } else { promise.reject(new Error('Token刷新失败,请重新登录')); } }); failedQueue = []; } async function refreshToken() { try { const response = await normalRoute.post("/users/refresh"); return response.data.accessToken; } catch (refreshError) { // 刷新失败时清空队列,避免请求一直挂起 processQueue(null); throw refreshError; } } privateRoute.interceptors.response.use( (response) => response, async (error) => { const originalConfig = error.config; // 跳过已重试过的请求,防止无限循环刷新 if (error.response?.status === 401 && !originalConfig._retry) { originalConfig._retry = true; if (!isRefreshing) { isRefreshing = true; try { const token = await refreshToken(); store.dispatch(setAccessToken(token)); // 更新当前请求的Token并重试 originalConfig.headers.authorization = `Bearer ${token}`; // 批量处理队列中所有等待的请求 processQueue(token); return privateRoute(originalConfig); } catch (refreshError) { // 刷新失败直接抛出错误,交给前端处理登录跳转 return Promise.reject(refreshError); } finally { isRefreshing = false; } } else { // 当前正在刷新Token,将请求加入队列等待结果 return new Promise((resolve, reject) => { failedQueue.push({ resolve: (newToken) => { originalConfig.headers.authorization = `Bearer ${newToken}`; resolve(privateRoute(originalConfig)); }, reject }); }); } } // 非401错误或已重试过的请求,直接抛出错误 return Promise.reject(error); } );
关键修复点说明
- 添加重试标记:给请求配置加
_retry属性,避免因刷新失败陷入无限循环。 - 队列Promise化:后续进入的请求会返回Promise,等待Token刷新完成后再执行重试,确保所有并行请求都能获取到新Token。
- 刷新失败兜底:当
refreshToken接口报错时,清空队列并抛出错误,避免请求一直处于等待状态。 - 完善错误链路:非401场景直接抛出错误,不干扰原有错误处理流程。
结合react-query使用时,保持默认的重试配置即可(默认开启重试),请求被拦截重试后,react-query会自动更新缓存数据。
内容的提问来源于stack exchange,提问作者DevRohan
相关产品推荐
相关产品推荐

