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;
相关推荐
90后的晨仔3 小时前
🚨XSS 攻击全解:什么是跨站脚本攻击?前端如何防御?
前端·vue.js
90后的晨仔3 小时前
Vue 模板语法完全指南:从插值表达式到动态指令,彻底搞懂 Vue 模板语言
前端·vue.js
90后的晨仔3 小时前
Vue 3 应用实例详解:从 createApp 到 mount,你真正掌握了吗?
前端·vue.js
SY_FC4 小时前
uniapp input 聚焦时键盘弹起滚动到对应的部分
javascript·vue.js·elementui
爱吃香蕉的阿豪4 小时前
SignalR 全解析:核心原理、适用场景与 Vue + .NET Core 实战
vue.js·microsoft·c#·.netcore·signalr
OpenTiny社区7 小时前
把 SearchBox 塞进项目,搜索转化率怒涨 400%?
前端·vue.js·github
ReturnTrue8688 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
风清云淡_A8 小时前
【REACT18.x】CRA+TS+ANTD5.X封装自定义的hooks复用业务功能
前端·react.js
@大迁世界8 小时前
第7章 React性能优化核心
前端·javascript·react.js·性能优化·前端框架
武昌库里写JAVA9 小时前
「mysql」Mac osx彻底删除mysql
vue.js·spring boot·毕业设计·layui·课程设计