axios学习之取消请求

100 阅读2分钟

用于记录

开发中经常会遇到请求重复的问题,当需要的资源一样的情况下,多次请求会造成服务器不必要的工作,为了更好的性能,需要取消请求的操作

在axios的请求拦截器中给当前请求添加controller,每个请求的controller唯一,所以需要在拦截器中定义

instance.interceptors.request.use(async function (config) {
  const controller = new AbortController();
  const signal = controller.signal;
  config.signal = signal;
  config.controller = controller;
  //删除之前url的请求
  await deletePending(config);
  // 将当前请求添加到 pending 中
  await addPending(config);
  return config;
});

响应之后同样也该删除

instance.interceptors.response.use(
  function (response) {
    deletePending(response.config);
    return response.data;
  },
  function (error) {
    if (error.code !== "ERR_CANCELED") {
      return Promise.reject(error);
    }
  }
);

定义deletePending和addPending方法,需要定义一个全局的map对象,用于关联请求和取消方法。

const pending = new Map();

export const addPending = (config) => {
  const newParams = { ...config.params };
  const newData = { ...config.data };
  const url = [
    config.url,
    JSON.stringify(newData),
    JSON.stringify(newParams),
    config.method,
  ].join("&");
  pending.set(url, config.controller);
};

export const deletePending = (config) => {
  const newParams = { ...config.params };
  const newData = { ...config.data };
  const url = [
    config.url,
    JSON.stringify(newData),
    JSON.stringify(newParams),
    config.method,
  ].join("&");
  if (pending.get(url)) {
    // 取消请求
    const cancel = pending.get(url);
    cancel.abort();
    pending.delete(url);
  }
};

另一个版本

// --------------------------
// 文件: request.js
// --------------------------
import axios from 'axios';

// 存储所有进行中的请求控制器 { key: AbortController }
const pendingRequests = new Map();

/**
 * 生成请求的唯一标识键
 * (可根据项目需求调整序列化规则)
 */
function generateReqKey(config) {
  const { method, url, params, data } = config;
  return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
}

/**
 * 添加请求到等待列表
 */
function addPendingRequest(config) {
  const key = generateReqKey(config);
  if (!pendingRequests.has(key)) {
    const controller = new AbortController();
    config.signal = controller.signal; // 挂载到请求配置
    pendingRequests.set(key, controller);
  } else {
    // 已存在相同请求,直接复用之前的 signal
    config.signal = pendingRequests.get(key).signal;
  }
}

/**
 * 移除已完成/已取消的请求
 */
function removePendingRequest(config) {
  const key = generateReqKey(config);
  if (pendingRequests.has(key)) {
    pendingRequests.delete(key);
  }
}

/**
 * 取消所有进行中的请求
 */
export function cancelAllRequests() {
  pendingRequests.forEach(controller => controller.abort());
  pendingRequests.clear();
}

/**
 * 取消指定请求
 */
export function cancelRequest(config) {
  const key = generateReqKey(config);
  if (pendingRequests.has(key)) {
    pendingRequests.get(key).abort();
    pendingRequests.delete(key);
  }
}

// 创建 Axios 实例
const instance = axios.create({
  timeout: 15000,
  // 其他全局配置...
});

// 请求拦截器 - 自动添加取消控制器
instance.interceptors.request.use(config => {
  // 自动取消重复请求 (需关闭则删除此行)
  cancelRequest(config); 

  addPendingRequest(config);
  return config;
}, error => {
  return Promise.reject(error);
});

// 响应拦截器 - 自动清理已完成请求
instance.interceptors.response.use(response => {
  removePendingRequest(response.config);
  return response;
}, error => {
  // 统一处理取消请求的错误
  removePendingRequest(error.config || {});
  
  if (axios.isCancel(error)) {
    return Promise.reject({ isCanceled: true }); // 自定义取消标识
  }
  
  // 其他错误处理...
  return Promise.reject(error);
});

export default instance;