源码重复请求拦截逻辑?

访客 源码剖析 2

前端源码中的幂等性逻辑深度解析

目录导读

  1. 为什么需要拦截重复请求? – 从用户体验到后端压力的现实痛点
  2. 重复请求的常见场景 – 用户误操作、网络重试、接口回调
  3. 源码级实现:四种主流拦截方案
    • 基于请求URL+参数的哈希缓存
    • 基于请求时间戳的节流防抖
    • 基于Promise状态的全局锁机制
    • 基于Axios拦截器的统一封装
  4. 源码示例与对比 – 从简单到完善的代码实现
  5. 常见问题与解答(Q&A) – 命中率、内存泄漏、并发场景
  6. SEO优化建议 – 元描述、标签、长尾关键词布局
  7. – 选择最适合你业务场景的拦截策略

为什么需要拦截重复请求?

在Web开发中,重复请求 是指用户或系统在短时间内对同一接口发起多次完全相同的请求,根据多家互联网公司的故障报告,重复请求导致的数据库写入错误、缓存击穿、分布式锁争用等问题,占线上事故的15%-20%。

核心痛点:

  • 用户体验:用户点击提交按钮后,页面无响应,再次点击导致多个请求同时发出,后端返回多条数据或执行多次操作(如重复下单、重复扣款)。
  • 后端压力:高并发下,重复请求会耗尽服务器连接池,导致正常请求被阻塞。
  • 数据一致性:重复的写操作可能导致数据库记录重复、库存扣减多次、支付回调重复处理。

重复请求的常见场景

场景 触发原因 案例
用户误操作 用户快速点击“提交”按钮 支付页面点击2次下单按钮
网络重试机制 前端axios/fetch自动重试 弱网环境下接口超时后自动重发
组件生命周期 页面组件卸载又挂载 React useEffect未清除定时器导致的重复请求
路由重复切换 用户快速切换同一页面 Tab页快速切换触发相同数据加载
轮询与推送 短时间收到多条同类型消息 WebSocket推送后前端又发起手动请求

源码级实现:四种主流拦截方案

基于请求URL+参数的哈希缓存

核心逻辑:用一个对象(Map)存储已发起的请求,以${method}:${url}:${JSON.stringify(params)}为key,存储一个Promise,当相同请求再次到来时,直接返回已有Promise。

const pendingRequests = new Map();
function requestWithCache(config) {
  const key = `${config.method}:${config.url}:${JSON.stringify(config.params || config.data)}`;
  if (pendingRequests.has(key)) {
    return pendingRequests.get(key); // 重复请求,返回同一Promise
  }
  const promise = axios(config).finally(() => {
    pendingRequests.delete(key); // 请求完成后清除缓存
  });
  pendingRequests.set(key, promise);
  return promise;
}

优点:简单高效,避免重复发起HTTP请求。
缺点:若请求参数相同但响应体期望不同(如携带不同token),会导致错误。

基于请求时间戳的节流防抖(按钮级别)

适用于表单提交场景,限制用户多次点击。

// 防抖:最后一次点击后延迟执行
function debounceSubmit(callback, delay = 300) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => callback.apply(this, args), delay);
  };
}
// 节流:固定时间间隔内只执行一次
function throttleSubmit(callback, interval = 1000) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      lastTime = now;
      callback.apply(this, args);
    }
  };
}

优点:对用户点击动作直接拦截,无需关心接口URL。
缺点:无法拦截由代码发起的重复调用(如setState后的自动重发)。

基于Promise状态的全局锁机制

核心:用一个Map记录当前正在请求的接口,请求完成后释放锁,适用于所有前端框架。

const requestLock = new Map();
function requestWithLock(config) {
  const key = config.url; // 也可加上方法
  if (requestLock.get(key)) {
    return Promise.reject(new Error('请求正在处理中,请勿重复提交'));
  }
  requestLock.set(key, true);
  return axios(config)
    .then(response => {
      requestLock.delete(key);
      return response;
    })
    .catch(error => {
      requestLock.delete(key);
      throw error;
    });
}

优点:语义清晰,确保同一时间只有一个请求正在进行。
缺点:若用户不等待完成直接刷新页面,锁可能一直存在,需配合页面卸载时清除。

基于Axios拦截器的统一封装(推荐)

最实用的生产级方案:在全局Axios实例中添加请求/响应拦截器,统一处理重复请求。

// interceptors.js
const pendingRequests = new Map();
axios.interceptors.request.use(config => {
  const key = `${config.method}:${config.url}`;
  if (pendingRequests.has(key)) {
    config.cancelToken = new axios.CancelToken(cancel => {
      cancel('重复请求已被拦截');
    });
  } else {
    pendingRequests.set(key, true);
  }
  return config;
});
axios.interceptors.response.use(
  response => {
    const key = `${response.config.method}:${response.config.url}`;
    pendingRequests.delete(key);
    return response;
  },
  error => {
    if (!axios.isCancel(error)) {
      const key = `${error.config?.method}:${error.config?.url}`;
      pendingRequests.delete(key);
    }
    return Promise.reject(error);
  }
);

优点:全局生效,无需修改每个组件;支持取消请求(CancelToken)。
缺点:需要合理设计key的粒度,避免误拦截。


常见问题与解答(Q&A)

Q1:拦截逻辑会否误伤正常的快速翻页请求?
A:确实可能,建议区分API类型:对GET请求(如数据列表)实现防抖而不是完全拦截;仅对POST/PUT/DELETE等写操作使用去重逻辑。

Q2:Map中存储的Promise何时被释放?
A:在上面的示例中,.finally() 或响应拦截器会在请求完成后主动删除key,若网络断开导致请求卡死,建议结合AbortController(现代浏览器)或超时时间自动清理。

Q3:如何防止内存泄漏?
A:

  • 限制Map大小(如超过1000个自动清理最早的10%)。
  • 使用WeakMap存储DOM绑定数据(按钮级别防抖)。
  • window.addEventListener('beforeunload', ...)中清空Map。

Q4:同一个请求在不同页面间如何共享拦截状态?
A:可以使用postMessageBroadcastChannelSharedWorker进行跨标签页通信,但大多数场景不需要,因为重复请求多发生在同一页面。


SEO优化建议

本文重点围绕 “源码重复请求拦截逻辑” 这一长尾关键词,同时覆盖以下相关搜索:

  • 前端重复请求处理方案
  • Axios拦截器重复请求
  • 防抖节流解决重复提交
  • 取消重复请求 前端最佳实践
  • 幂等性接口前端实现

元描述

深入解析前端源码中的重复请求拦截逻辑,从用户误操作到后端压力,提供4种实战方案(哈希缓存、防抖节流、全局锁、Axios拦截器),附带完整代码示例与性能优化技巧,适合React/Vue/Axios开发者。

内链建议

  • 文章内可链接“幂等性设计”、“Axios CancelToken”等子话题。
  • 底部推荐相关文章:“如何实现后端幂等性接口”、“前端请求超时重试机制”。

重复请求拦截是前端工程化中不可忽视的一环,根据你的业务场景选择合适方案:

  • 用户低频率操作(如支付)→ 防抖+全局锁
  • 高并发数据加载(如列表)→ 路由级别取消+节流
  • 全局治理需求(如中后台系统)→ Axios拦截器+URL哈希缓存

行动建议

  1. 先用防抖处理所有提交按钮。
  2. 在axios拦截器中加入请求去重,建议仅对POST/PUT生效。
  3. 对GET请求只在同一组件内(如useEffect)使用AbortController取消上一轮请求

最终的源码实现应结合你的项目技术栈(React/Vue/Angular),并定期审查Map的清理逻辑,防止内存泄漏。

标签: 重复请求

抱歉,评论功能暂时关闭!