Skip to content

取消重复请求

使用唯一标识和Map存储

通过为每个请求生成一个唯一标识,并将它们存储在Map中,可以检测并取消重复的请求

js
const pendingRequests=new Map();

function generateKey(config) {
  // 根据请求方法和 URL 生成唯一的键
  return `${config.method}:${config.url}`;
}

function addRequest(config) {
  const key = generateKey(config);
  if (pendingRequests.has(key)) {
    // 如果请求已存在则取消
    const cancelToken = pendingRequests.get(key);
    cancelToken.cancel('Request canceled due to duplicate.');
  }

  const cancelToken = axios.CancelToken.source();
  config.cancelToken = cancelToken.token;
  pendingRequests.set(key, cancelToken);
}

function removeRequest(config) {
  const key = generateKey(config);
  pendingRequests.delete(key);
}
js
// 使用示例
axios.interceptors.request.use((config) => {
  addRequest(config);
  return config;
});

axios.interceptors.response.use(
  (response) => {
    removeRequest(response.config);
    return response;
  },
  (error) => {
    removeRequest(error.config || {});
    return Promise.reject(error);
  }
);

通过axios的请求和响应拦截器,管理每个请求的取消标识,如果请求重复,之前的请求会被取消

使用AbortController

AbortController 是现代浏览器支持的一种用于取消 fetch 请求的方法。可以将 AbortController 与 fetch 请求结合使用,在发起新的请求时取消未完成的相同请求。

js
const controllers = new Map();

function fetchWithCancel(url, options = {}) {
  // 如果请求已经存在则取消
  if (controllers.has(url)) {
    controllers.get(url).abort();
  }

  const controller = new AbortController();
  const signal = controller.signal;
  controllers.set(url, controller);

  return fetch(url, { ...options, signal })
    .then((response) => {
      controllers.delete(url);
      return response;
    })
    .catch((error) => {
      controllers.delete(url);
      if (error.name === 'AbortError') {
        console.log('Request was canceled');
      } else {
        throw error;
      }
    });
}

// 使用示例
fetchWithCancel('/api/data');
fetchWithCancel('/api/data'); // 第二个请求会取消第一个

防抖debounce 和节流throttle

防抖——一段时间内只执行最后一次操作,适合用户停止输入后再发送请求

js
function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

// 使用示例
const search = debounce((query) => {
  fetchWithCancel(`/api/search?query=${query}`);
}, 300);

节流——在规定事件内只执行一次,适合滚动或点击等频繁触发的请求

js
function throttle(fn, interval) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

// 使用示例
const fetchData = throttle(() => {
  fetchWithCancel('/api/data');
}, 1000);

标记唯一请求ID

对于某些 API 请求,可以为每次请求添加唯一 ID,通过在后端检查该 ID 来过滤重复请求。这种方法通常需要后端配合。

示例:

  • 在每次请求中生成唯一 ID 并传给后端,后端检查是否已有相同的请求正在处理中,如果有则忽略新请求。

  • 可以用 UUID 或其他方法生成唯一请求标识符,并通过请求头或请求体传递。

设置请求队列(请求去重)

对于短时间内发起的相同请求,可以使用请求队列实现请求去重

js
const requestQueue = {};

function uniqueRequest(url) {
  if (requestQueue[url]) {
    return requestQueue[url]; // 返回已存在的请求
  }
  
  requestQueue[url] = fetch(url)
    .then((response) => response.json())
    .finally(() => {
      delete requestQueue[url]; // 请求完成后移出队列
    });

  return requestQueue[url];
}

// 使用示例
uniqueRequest('/api/data');
uniqueRequest('/api/data'); // 第二次请求会复用第一次请求