防止接口重复请求——axiso cancelToken

项目中按钮请求接口因用户短时间连续点击致系统异常,考虑在前端拦截相同请求。先尝试禁用按钮思路,但存在问题放弃。后决定在axiso请求拦截器中处理,记录请求url,若重复则取消请求,成功后移除记录值。还介绍了具体实现及js频繁点击事件的防抖和节流。

一、背景

项目中按钮请求接口出现用户短时间内连续点击导致系统出现异常。考虑在前端对相同的请求进行拦截。

二、实现思路

1.按钮点击之后禁用,间隔一定时间或接口请求完成后启用,达到防止点击的目的。

    ①禁用一定时间后重新启用的时机不好判断,可能接口还没请求结束就启用的话还会出现上述问题

    ②在接口的请求回调中禁用,因为项目已经是上线项目,设计范围太广,修改工作量较大。

    综上所述,放弃使用禁用按钮的思路

2.考虑在axiso请求拦截器中处理相同请求

   在接口请求时记录请求的url,再次请求时判断当前的url是否在记录内。如果在记录内则取消请求。当请求成功后则移除记录值。从而达到不允许相同接口同时请求的目的。

三、具体实现

1.增加pending字段用来保存请求的接口

2.声明 cancelToken  

let pending = []; // 声明一个数组用于存储每个请求的取消函数和axios标识
let cancelToken = axios.CancelToken;
let removePending = (config) => {
  for (let i in pending) {
    if (pending[i].url === config.url) { // 在当前请求在数组中存在时执行取消函数
      pending[i].f(); // 执行取消操作
      pending.splice(i, 1); // 把pending记录删掉
    }
  }
}
instance.interceptors.request.use(config => {
      removePending(config); // 在一个axios发送前执行一下判定操作,在removePending中执行取消操作
      config.cancelToken = new cancelToken(function executor (c) { // 本次axios请求的配置添加cancelToken
        pending.push({
          url: config.url,
          f: c
        });
      })
      return config
    })
// response 拦截器
    instance.interceptors.response.use(
      response => {
        removePending(response.config); // 在一个axios响应后再执行一下取消操作,把已经完成的请求从pending中移除
      },
      error => {
        if (error.response) {
          
        } else if (error.__CANCEL__) {
          console.log('操作过于频繁,稍后重试')
        } else {
          Message.error('请求出错')
        }
        return Promise.reject(error) // 返回接口返回的错误信息
      }
    )

扩展:js的频繁点击事件的防抖和节流

 

### Axios 中处理请求竞态问题的方法 在实际开发过程中,前端应用可能会遇到因多次快速触发相同接口而引发的 **竞态问题**[^2]。这种情况下,多个请求可能同时存在并返回数据的时间顺序不可控,从而导致界面显示异常或其他逻辑错误。 为了有效解决这一问题,可以利用 `axios` 提供的取消功能来管理重复请求。以下是具体的解决方案: #### 1. 使用 CancelToken 实现请求取消机制 `axios` 支持通过 `CancelToken` 来取消未完成的请求。可以通过创建一个全局存储对象(如数组),记录当前所有的待处理请求。当新请求发起时,检查是否有相同的旧请求正在执行;如果有,则取消该旧请求后再发送新的请求。 ```javascript // 定义一个用于存储所有 pending 请求的对象 const pendingRequests = new Map(); function addPendingRequest(config, cancelTokenSource) { const url = config.url + JSON.stringify(config.params || {}); if (!pendingRequests.has(url)) { pendingRequests.set(url, cancelTokenSource); } } function removePendingRequest(config) { const url = config.url + JSON.stringify(config.params || {}); if (pendingRequests.has(url)) { const source = pendingRequests.get(url); source.cancel('Operation canceled due to a newer request.'); pendingRequests.delete(url); } } ``` 上述代码片段定义了一个简单的工具函数集合,用来管理和清理重复请求[^3]。 #### 2. 封装 Axios 实例以支持自动取消重复请求 将上述逻辑集成到自定义封装的 `axios` 方法中,确保每次调用 API 前都会清除潜在的竞争状态。 ```javascript import axios from 'axios'; const instance = axios.create({ baseURL: '/api', timeout: 5000, }); instance.interceptors.request.use((config) => { // 创建一个新的 CancelToken Source 对象 let cancelTokenSource; if (!config.noCancelDuplicate) { cancelTokenSource = axios.CancelToken.source(); config.cancelToken = cancelTokenSource.token; // 移除之前的同名请求 removePendingRequest(config); // 添加当前请求至队列 addPendingRequest(config, cancelTokenSource); } return config; }, error => Promise.reject(error)); instance.interceptors.response.use( response => { // 成功响应后移除此请求 removePendingRequest(response.config); return response; }, error => { if (axios.isCancel(error)) { console.warn('Request was cancelled:', error.message); } else { // 非取消错误则正常抛出 return Promise.reject(error); } } ); export default instance; ``` 在此封装中引入了拦截器模式,分别针对请求阶段和响应阶段进行了增强处理[^4]。 --- ### 总结 通过合理运用 `axios` 的内置特性——即基于 `Promise` 和可选参数中的 `cancelToken` 属性,能够有效地规避由频繁操作引起的竞态隐患。这种方法不仅提高了用户体验的一致性和稳定性,还减少了不必要的网络流量消耗以及服务器负载压力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值