如何优雅的取消axios各种请求?

1,329 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情

一:引言

小伙伴们在日常开发中肯定经常使用axios,在使用过程中大家有没有考虑到axios的两个请求场景?

取消重复请求

同一个接口数据重复请求了n次,我们只需要第一次请求,其它请求需要全部取消

取消上一次请求

在请求当前接口数据时,取消上一次接口的请求。使用场景如tab选项卡,假设点击每个选项卡都会发起一个接口请求,如果最新点击的tab接口数据快于上一次tab接口数据,那么最新的数据渲染就是上一个接口的数据了,此时我们在tab选项卡切换的时候要取消上一次的请求。

二:axios中取消请求api

网上关于axios取消请求的介绍太多了,但是大部分都是cancelToken的介绍,很不幸这个方法已经废弃了。但是网上还充斥着许多关于cancelToken的介绍,想到这心里顿时一万个草泥马。

正确取消axios请求的姿势

//1.创建取消请求对象
const controll = new AbortController()
//2.在axios参数内部添加signal属性
axios({
    method:"get",
    url:"xxx",
    signal:controll.signal,
})
//3.在需要取消请求的地方直接调用
controll.abort()

根据上面三步走,狗都能取消axios请求了。哈哈哈

三:取消重复请求

取消重复请求,实际上我们可以配合按钮的禁用属性灵活的解决,当请求发出时,禁用按钮点击。当请求完成时再恢复按钮点击。今天我们重点是考虑如何基于axios拦截器取消重复请求。

设计思路

  1. 配置axios的请求拦截器和响应拦截器
  2. 设计一个根据请求参数生成唯一字符串的函数,此函数我们可以分析请求是否属于重复请求。
  3. 设计一个数组,保存所有请求生成的字符串
  4. 在请求拦截器处,根据当前请求参数生成字符串,然后判断字符串是否在数组存在。如果存在说明请求重复了,直接取消当前请求,反之正常请求并将字符串添加到数组
  5. 在响应拦截器处,在请求响应后,删除数组中对应请求保存的字符串。

axios封装请求

import axios from "axios";

const instance = axios.create({
  baseURL:"/api",
  timeout:5000
})

//任务队列
let que = []
//根据每次的请求生成hash字符串
function createHash(config) {
  const {data,params,method,url} = config
  if(data) data = JSON.stringify(data)
  if(params) params = JSON.stringify(params)
  return [method,url,data,params].join("*")
}

/**请求拦截 */
instance.interceptors.request.use(function (config) {
  //1.请求前生成hash
  let hash = createHash(config)
  const {controll} = config
  //2.如果hash存在,取消本次请求
  if(que.includes(hash)) {
    console.log("重复了")
    controll.abort()
  }
  //3.否则,添加hash,正常执行
  que.push(hash)
  return config;
}, function (error) {
  return Promise.reject(error);
});
/**响应拦截 */
instance.interceptors.response.use(function (response) {
  //1.执行完成,从队列删除hash
  const hash = createHash(response.config)
  que.splice(que.indexOf(hash),1)
  return response;
}, function (error) {
  //2.执行完成,从队列删除hash(虽然错误,也要删除hash)
  if(error.message!="canceled") {
    const hash = createHash(error.config)
    que.splice(que.indexOf(hash),1)
  }
  return Promise.reject(error);
});
export default instance

App.vue进行测试

<template>
  <div>
    <!--取消重复请求例子-->
    <h2>测试取消重复请求案例</h2>
      <div @click="getData()">点击请求数据</div>
  </div>
</template>

<script setup>
  import instance from './services/index';
  /**模拟取消重复请求 */
  const getData = async() => {
    const controll = new AbortController()
    try{
        let info = await instance({
        method:"get",
        url:"/",
        signal:controll.signal,
        controll
      })
      console.log(info)
    }catch(e){
      console.log(e)
    }
  }
</script>

界面演示

我们连续点击3次,可以看见第一次请求后,之后的两次请求都被直接取消了。最后只输出最后一次的请求数据

3.gif

四:取消上一次请求

上面已经提到了取消上一次请求的使用场景,我们看下面的例子

image.png

此处我们模拟一个tab选项卡,点击每一个选项卡都会发起一次数据请求,那么如何取消上一次请求? 由于取消上一次请求的设计可以通过一个控制变量实现。因此我们简单封装了axios

设计思路

取消上一次请求的设计,核心就是保存上一次取消请求的控制变量,我们可以直接使用一个变量保存上一次请求的取消控制器的变量,每次在请求新接口数据时,如果控制变量非空可以直接取消上一次请求。

index2.js

import axios from "axios";

const instance = axios.create({
  baseURL:"/api",
  timeout:5000
})

export default instance

App.vue

<template>
  <div>
    <h2>测试取消上一次请求案例</h2>
    <div class="box">
      <div class="title">
        <div @click="getInfo(1)">请求接口1</div>
        <div @click="getInfo(2)">请求接口2</div>
      </div>
      <div class="content">
        {{show}}
      </div>
    </div>
  </div>
</template>

<script setup>
  import instance2 from './services/index2';
  import {ref} from "vue"
  /**模拟取消上一次请求 */
  let pre = null//1.标记前一次的请求控制器
  const show = ref()
  const getInfo = async(data) => {
    //2.每次请求都定义自己的控制器
    const controll = new AbortController()
    //3.如果上次的非空,取消上次请求
    if(!pre) pre = controll
    else {
      console.log("取消")
      pre.abort()
      pre = controll
    }
    try{
        let info = await instance2({
        method:"get",
        url:"/",
        signal:controll.signal
      })
      if(data==1) show.value = 1
      else show.value = 2
    }catch(e){
      console.log(e)
    }
  }
</script>

<style>
  .box{
    border:1px solid red;
    width: 300px;
    height: 300px;
  }
  .title{
    display: flex;
    justify-content: space-around;
  }
</style>

界面展示

我们频繁的在接口1和接口2上点击,最后只请求了最后一次请求。

3.gif

项目地址

项目小demo已经放在github上,需要的伙伴可以直接下载进行演示。

github地址