cancelToken中断请求解决重复请求的问题

101 阅读2分钟

工作中偶尔遇到切换tab请求数据的场景,然而当每个Tab下的数据量较大时接口返回较慢;此时测试也不按正常思维测试😂来回操作切换Tab导致页面显示的数据错误的问题。就这样喜提BUG单一个🤦‍♂️

image.png

解决
cancelToken是什么

CancelToken是Axios提供了一个实用的特性,它允许我们取消一个或多个正在进行的请求。

基础用法

在axios请求配置中添加CancelToken申明与其对应使用的isCancel 是判断是否为主动中断请求的状态值。可使用isCancel状态做其他逻辑操作。

 axios
      .get(url, {
        cancelToken: new axios.CancelToken(function executor(c) {
          cancel = c; // 将取消函数赋值给外部变量
        }),
      })
      .catch((err) => {
        if (axios.isCancel(err)) {
          console.log('请求已取消!', err);
        } else {
          console.log('其他错误!', err);
        }
      });

全局封装使用
  • 封装类
import axios from 'axios'
export default class CancelRequest {
    constructor() {
        this.pendingRequest = new Map()
        // 控制重复请求的接口地址
        this.controlUrl = ['/resource-catalog/video/channel/square/page/new', '/mission/approval/selectMissionChannelDraftByPage']
    }
    // 根据请求信息生成唯一标识key
    geterateReqKey(config) {
        const { url, method } = config
        return this.controlUrl.includes(url) ? [url, method].join('&') : ''
    }
    // 把正在请求的接口信息添加到pendingRequest对象中
    addPendingRequest(config) {
        const requestKey = this.geterateReqKey(config)
        config.cancelToken =
            config.cancelToken ||
            new axios.CancelToken((cancel) => {
                if (!this.pendingRequest.has(requestKey) && requestKey != '') {
                    // 把请求取消方法作为 map 值存起来
                    this.pendingRequest.set(requestKey, cancel)
                }
            })
    }
    // 检查是否存在重复请求,若存在则取消前一次请求
    removePendingRequest(config) {
        const requestKey = this.geterateReqKey(config)
        if (this.pendingRequest.has(requestKey)) {
            const cancel = this.pendingRequest.get(requestKey)
            // 取消请求
            cancel(requestKey)
            // 删除map中对应的属性
            this.removeRequestKey(config)
        }
    }
    // 从pendingRequest中删除对应的key
    removeRequestKey(config) {
        const requestKey = this.geterateReqKey(config)
        this.pendingRequest.delete(requestKey)
    }
}

  • 使用时引入类

import RequestCanceller from './requestCanceller'
let requestCanceller = new RequestCanceller()

  • 处理请求拦截
  axiosInstance.interceptors.request.use(
        function (config) {
            // 在请求开始之前检查先前的请求,如果是重复请求,删除之前的
            requestCanceller.removePendingRequest(config)
            // 如果不存在 就将当前请求添加到pendingRequest
            requestCanceller.addPendingRequest(config)
            return config
        },
        function (error) {
            return Promise.reject(error)
        }
    )
  • 处理响应拦截
    axiosInstance.interceptors.response.use(
        (response) => {
            // 移除成功请求记录
            requestCanceller.removeRequestKey(response.config)
            if (response.status === 200) {
                if (response && response.data && response.data.code === 413) {
                    debounce(() => {
                        Message.warning({
                            message: '该用户无此权限',
                            showClose: false,
                        })
                    })
                    return
                } else {
                    return response.data
                }
            }
            return response
        },
        (error) => {
            // 失败时也需要移除请求记录
            requestCanceller.removeRequestKey(error.config || {})
        }
    )
缺点
  • 性能开销‌:每次请求都需要创建和管理取消令牌,令牌管理会带来一定的内存开销‌。
  • 用户体验‌:netWork会显示接口cancel状态,会让用户觉得是页面数据接口出错。