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 小时前
vue前端面试题——记录一次面试当中遇到的题(10)
前端·vue.js·面试
Lazy_zheng1 小时前
一场“数据海啸”,让我重新认识了 requestAnimationFrame
前端·javascript·vue.js
crary,记忆1 小时前
MFE: React + Angular 混合demo
前端·javascript·学习·react.js·angular·angular.js
vx Biye_Design1 小时前
servlet宠物医院管理系统-计算机毕业设计源码77418
java·vue.js·spring·servlet·eclipse·mybatis
一枚前端小能手1 小时前
🔥 重学Vue之computed、watch、watchEffect原理与用途详解
前端·javascript·vue.js
new出一个对象2 小时前
vue实现打印PDF文档
javascript·vue.js·pdf
练习前端两年半2 小时前
Vue3组件二次封装终极指南:动态组件+h函数的优雅实现
前端·vue.js
周家大小姐.2 小时前
vue实现模拟deepseekAI功能
前端·javascript·vue.js
一个处女座的程序猿O(∩_∩)O3 小时前
React 多组件状态管理:从组件状态到全局状态管理全面指南
前端·react.js·前端框架
鹏多多3 小时前
用useTransition解决React性能卡顿问题+实战例子
前端·javascript·react.js