axios 取消上次重复请求

在实际开发中,可能会遇到同一个接口被多次调用的情况(例如用户快速点击按钮或页面频繁刷新)。为了避免重复请求浪费资源,可以通过以下设计思路实现取消重复请求。

1.实现思路
  1. 唯一标识请求

    为每个请求生成一个唯一标识符(requestKey),通常由请求的 method 和 url 组成。如果需要更精确,可以将 params 或 data 也纳入标识符。

  2. 存储请求

    使用一个数据结构(如 Map)来存储当前正在进行的请求及其对应的 AbortController 实例。

  3. 取消重复请求

    在发送新请求时,检查 Map 中是否已经存在相同的 requestKey。如果存在,调用 AbortController.abort() 取消上一个请求,并从 Map 中移除。如果不存在,则将当前请求的 AbortController 存入 Map。

  4. 移除已完成的请求

    在请求完成或失败时,从 Map 中移除对应的 requestKey。

2.实现步骤
  1. 创建 Map 存储请求
    使用 Map 数据结构存储请求的唯一标识符和对应的 AbortController。
js 复制代码
const pendingRequests = new Map<string, AbortController>();
  1. 生成请求的唯一标识符
    定义一个函数,根据请求的 method 和 url 生成唯一标识符。
js 复制代码
const getRequestKey = (config: AxiosRequestConfig): string => {
  return `${config.method}:${config.url}`;
};
  1. 请求拦截器
    在请求拦截器中:检查 Map 中是否存在相同的 requestKey。如果存在,取消上一个请求。
    如果不存在则为当前请求创建一个新的 AbortController,并存入 Map。
js 复制代码
axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
  const requestKey = getRequestKey(config);

  // 如果存在相同的请求,则取消上一次请求
  if (pendingRequests.has(requestKey)) {
    const controller = pendingRequests.get(requestKey);
    controller?.abort(); // 取消上次请求
    pendingRequests.delete(requestKey); // 从 Map 中移除
  }

  // 为当前请求创建一个新的 AbortController
  const controller = new AbortController();
  config.signal = controller.signal;

  // 将当前请求存储到 Map 中
  pendingRequests.set(requestKey, controller);

  return config;
});
  1. 响应拦截器
    在响应拦截器中:请求完成后,从 Map 中移除对应的 requestKey。
js 复制代码
axiosInstance.interceptors.response.use(
  (response: AxiosResponse) => {
    const requestKey = getRequestKey(response.config);
    pendingRequests.delete(requestKey); // 请求完成后移除
    return response.data;
  },
  (error) => {
    if (axios.isCancel(error)) {
      console.warn("Request canceled:", error.message);
    } else {
      const requestKey = getRequestKey(error.config);
      pendingRequests.delete(requestKey); // 请求完成后移除
      console.error("API Error:", error.response?.data || error.message);
    }
    return Promise.reject(error);
  }
);
  1. 完整代码实现
js 复制代码
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";

// 用于存储请求的 Map
const pendingRequests = new Map<string, AbortController>();

// 生成请求的唯一标识
const getRequestKey = (config: AxiosRequestConfig): string => {
  return `${config.method}:${config.url}`;
};

// 创建 Axios 实例
const axiosInstance = axios.create({
  baseURL: import.meta.env.VITE_BASE_URL, // 替换为你的 API 基础 URL
  timeout: 50000, // 请求超时时间
  headers: {
    "Content-Type": "application/json",
  },
});

// 请求拦截器
axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
  const requestKey = getRequestKey(config);

  // 如果存在相同的请求,则取消上一次请求
  if (pendingRequests.has(requestKey)) {
    const controller = pendingRequests.get(requestKey);
    controller?.abort(); // 取消上次请求
    pendingRequests.delete(requestKey); // 从 Map 中移除
  }

  // 为当前请求创建一个新的 AbortController
  const controller = new AbortController();
  config.signal = controller.signal;

  // 将当前请求存储到 Map 中
  pendingRequests.set(requestKey, controller);

  return config;
});

// 响应拦截器
axiosInstance.interceptors.response.use(
  (response: AxiosResponse) => {
    const requestKey = getRequestKey(response.config);
    pendingRequests.delete(requestKey); // 请求完成后移除
    return response.data;
  },
  (error) => {
    if (axios.isCancel(error)) {
      console.warn("Request canceled:", error.message);
    } else {
      const requestKey = getRequestKey(error.config);
      pendingRequests.delete(requestKey); // 请求完成后移除
      console.error("API Error:", error.response?.data || error.message);
    }
    return Promise.reject(error);
  }
);

export default axiosInstance;
相关推荐
梦魇泪1 小时前
实践项目开发-hbmV4V20250407-跨平台开发框架深度解析与VSCode一站式开发实践
vscode·react.js·taro
vvilkim1 小时前
深入解析React.lazy与Suspense:现代React应用的性能优化利器
前端·react.js·前端框架
CF14年老兵2 小时前
你可以使用这些技巧将 Web 应用程序的大小减小数倍
vue.js·angular.js
小lan猫2 小时前
【实战】 Vue 3、Anything LLM + DeepSeek本地化项目(三)
前端·vue.js
许妹l2 小时前
运行vue2源码,最适合的nodejs版本和yarn版本
vue.js
许妹l2 小时前
vmCount 在 Vue 2 中的完整作用机制
vue.js
小鱼冻干2 小时前
vue-router(v4.x)核心知识点
前端·vue.js
格子惊蛰版2 小时前
Vue2 项目如何让代码更优雅(vue-property-decorator 的使用)
前端·vue.js
这个一个非常哈2 小时前
VUE篇之,实现锚点定位,滚动与导航联动
前端·javascript·vue.js
小鱼冻干2 小时前
Vue组件化开发三
前端·vue.js