You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止axios在已有refresh token请求待处理时重复发起刷新请求

解决方案

核心通过刷新请求锁+待重试请求队列实现,避免重复发起刷新token请求,改造后的代码如下:

// 新增两个全局变量,放在createResponseInterceptor作用域外侧
let isRefreshing = false;
let refreshSubscribers: ((token: string) => void)[] = [];

// 新增:订阅重试请求的方法
const subscribeTokenRefresh = (cb: (token: string) => void) => {
  refreshSubscribers.push(cb);
};

// 新增:刷新成功后批量触发所有待重试请求
const onRefreshed = (token: string) => {
  refreshSubscribers.forEach(cb => cb(token));
  refreshSubscribers = [];
};

const createResponseInterceptor = () => {
const interceptor = axiosClient.interceptors.response.use(
    (response) => {
        return response;
    },
    (error: AxiosError) => {
        const originalRequest = error.config;
        if (error.response && error.response.status !== 401) {
            return Promise.reject(error);
        }

        axiosClient.interceptors.response.eject(interceptor);

        console.info('token expired', error.response?.config);

        // 核心改造逻辑
        if (!isRefreshing) {
          isRefreshing = true;
          return axiosClient
              .patch('/auth', {
                  refreshToken: localStorage.getItem('refresh_token'),
              })
              .then((res) => {
                  if (res.status === 200) {
                      const newAccessToken = res.data.accessToken;
                      localStorage.setItem(
                          'access_token',
                          newAccessToken
                      );

                      axiosClient.defaults.headers['Authorization'] = 
                          newAccessToken;
                      originalRequest.headers['Authorization'] = 
                          newAccessToken;

                      // 触发所有等待中的请求重试
                      onRefreshed(newAccessToken);
                      return axiosClient(originalRequest);
                  }
              })
              .catch((err) => {
                  if (err.response && err.response.status === 401) {
                      localStorage.removeItem('access_token');
                      localStorage.removeItem('refresh_token');

                      window.location.href = '/login';

                      return Promise.reject(err);
                  } else {
                      console.info('error failed');
                      axiosClient.interceptors.response.eject(interceptor);

                      return Promise.reject(err);
                  }
              })
              .finally(() => {
                // 释放刷新锁
                isRefreshing = false;
                createResponseInterceptor();
              });
        } else {
          // 已经有刷新请求在处理,将当前请求加入等待队列
          return new Promise((resolve) => {
            subscribeTokenRefresh((token: string) => {
              originalRequest.headers['Authorization'] = token;
              resolve(axiosClient(originalRequest));
            })
          })
        }
    }
);
};

关键逻辑说明

  • isRefreshing 是全局状态锁,保证同一时间最多只有一个刷新token的请求被发起
  • refreshSubscribers 队列会暂存所有刷新token期间返回401的请求,等token刷新成功后,统一用新的token批量重试所有暂存的请求
  • 后续触发401的请求不会重复调用刷新接口,只会等待锁释放后拿到新token重试原有请求

内容的提问来源于stack exchange,提问作者arnodorian067

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 07:45:04