12月14, 2021

axios防抖请求

axios防抖请求

什么叫做防抖相信很多人都不陌生把 在这里还是稍作解释,防抖不是所谓的防抖动而是重复,就是在你网络很卡的时候点击一个按钮重复发送请求,之前某宝就有这种现象明明只支付一次却付了两次钱,就是因为网络卡,重复发送请求,然后在输入指纹的时候,一次就把两次请求都做了,下面进入正文

1.PC端 - 用户双击搜索按钮,可能会触发两次搜索请求 2.移动端 - 因移动端没有点击延迟,所以极易造成误操作或多操作,造成请求重发 以上情况有可能在有Loading遮罩时依然发生,所以我们要考虑前端阻止重复请求的方法。

核心——CancelToken

在Axios中取消请求最核心的方法是CanelToken。在官网文档中有写到两种方法使用CancelToken,这里简单粘贴出来,并增加了注释 方法一

const CancelToken = axios.CancelToken; 
const source = CancelToken.source();  //实例化

axios.get('/user/12345', {
  // 必须对请求进行cancelToken设置
  cancelToken: source.token
}).catch(function (thrown) {
  // 如果请求被取消则进入该方法判断
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

// 取消上面的请求
// source.cancel('messge') message为可选项,必须为String
source.cancel('Operation canceled by the user.');  //cancel函数取消请求

方法二

原理通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  // 在options中直接创建一个cancelToken对象
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});

// 取消上面的请求
cancel();

简单来写

var CancelToken = axios.CancelToken;
    var source = CancelToken.source();
    axios.get('/user/12345', {//get请求在第二个参数
     cancelToken: source.token
    }).catch(function(thrown) {
    });
    axios.post('/user/12345', {//post请求在第三个参数
     name: 'new name'
    }, {
     cancelToken: source.token
    });
    source.cancel('不想请求了')

注意,get请求的时候,cancelToken是放在第二个参数里;post的时候,cancelToken是放在第三个参数里。

// 正在进行中的请求列表
let reqList = []

/**
 * 阻止重复请求
 * @param {array} reqList - 请求缓存列表
 * @param {string} url - 当前请求地址
 * @param {function} cancel - 请求中断函数
 * @param {string} errorMessage - 请求中断时需要显示的错误信息
 */
const stopRepeatRequest = function (reqList, url, cancel, errorMessage) {
  const errorMsg = errorMessage || ''
  for (let i = 0; i < reqList.length; i++) {
    if (reqList[i] === url) {
      cancel(errorMsg)
      return
    }
  }
  reqList.push(url)
}

/**
 * 允许某个请求可以继续进行
 * @param {array} reqList 全部请求列表
 * @param {string} url 请求地址
 */
const allowRequest = function (reqList, url) {
  for (let i = 0; i < reqList.length; i++) {
    if (reqList[i] === url) {
      reqList.splice(i, 1)
      break
    }
  }
}

const service = axios.create()

// 请求拦截器
service.interceptors.request.use(
  config => {
    let cancel
      // 设置cancelToken对象
    config.cancelToken = new axios.CancelToken(function(c) {
        cancel = c
    })
    // 阻止重复请求。当上个请求未完成时,相同的请求不会进行
    stopRepeatRequest(reqList, config.url, cancel, `${config.url} 请求被中断`)
    return config
  },
  err => Promise.reject(err)
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    // 增加延迟,相同请求不得在短时间内重复发送
    setTimeout(() => {
      allowRequest(reqList, response.config.url)
    }, 1000)
    // ...请求成功后的后续操作
    // successHandler(response)
  },
  error => {
    if (axios.isCancel(thrown)) {
      console.log(thrown.message);
    } else {
      // 增加延迟,相同请求不得在短时间内重复发送
      setTimeout(() => {
        allowRequest(reqList, error.config.url)
      }, 1000)
    }
    // ...请求失败后的后续操作
    // errorHandler(error)
  }
)

一些小细节

1.为什么没用前文方法2中的代码进行cancelToken设置? axios的文档中有一条备注:

Note: you can cancel several requests with the same cancel token. 你可以使用相同的Token来取消多个请求

所以我不想在每个请求前都new一个新的对象 请务必使用方法2,保证每次cancel都能正确执行。之前方法会导致当出现cancel后,后续请求也会持续cancel

2.为什么在response中需要增加延迟? 因为不想让用户在极短的时间 内重复进行相同请求。 请注意,在response中阻止请求和在request中的阻止请求是两个概念: request中是阻止上个请求 未完成 时又开始了相同的请求 response中是阻止上个请求 完成后 一段时间内不允许相同请求

3.我能否在cancel时传递一个对象,而不仅仅是message? 以官方提供的接口来看是不可以的,你可以仿照官方源码进行重新封装,或者使用第三方插件,或者使用另一个方法:将对象转换为JSON字符串,然后在需要的地方再转换回来

本文链接:https://587v5.com/post/axios-fang-dou-qing-qiu.html

Comments