axios-取消重复请求--CancelToken&AbortController

CancelToken

CancelToken是axios提供的生成取消请求的函数。 在实战中常见的用法如下:

  1. 通过axios.CancelToken.source生成取消令牌token和取消方法cancel
js 复制代码
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})
source.cancel('取消操作');

post请求中cancelToken作为第三个参数,get请求中cancel作为第二个参数
  1. 通过axios.CancelToken构造函数生成取消函数
ini 复制代码
let cancel;
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});
cancel();

以上的代码块都是在单个请求方法中操作,接下我们把方法注入封装的axios.

新建requestCancel.js,处理各接口请求时的cancel方法新增与剔除。

js 复制代码
import axios from 'axios'  
export default class CancelRequest {  
constructor() {  
this.pendingRequest = new Map()  
}  
// 根据请求信息生成唯一标识key ,这里认为同一路径,同意请求方式为相同接口 也可控制到参数按需设置  
geterateReqKey(config) {  
const { url, method } = config  
return [url, method].join('&')  
}  
// 把当前请求信息添加到pendingRequest对象中  
addPendingRequest(config) {  
const requestKey = this.geterateReqKey(config)  
config.cancelToken =  
config.cancelToken ||  
new axios.CancelToken((cancel) => {  
if (!this.pendingRequest.has(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)  
}  
}

修改axios 的封装文件

js 复制代码
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) {  
return response.data  
}  
return response  
},  
(error) => {  
if(axios.isCancel(error)){
 console.log('请求取消');
}
// 失败时也需要移除请求记录  
requestCanceller.removeRequestKey(error.config || {})  
//错误码处理  
return permission(error)  
}  
)

需要注意的是在catch中捕获异常时,应该使用axios.isCancel()判断当前请求是否是主动取消的,以此来区分普通的异常逻辑

其实axiosv0.22.0之前取消请求是使用的cancel token API,而在v0.22.0开始 cancel token已经被打上了废弃 的标志 用的是AbortController

AbortController

AbortController是一个DOM API。MDN上对它的介绍是 AbortController接口表示一个控制器对象,允许根据需要终止一个或多个Web请求,使用方法如下:

js 复制代码
//该方法的用法与cancelToken类似

const controller = new AbortController();
 
axios.get('/foo/bar', {
//signal 属性是controller 与 fetch 请求相关联,方便我们通过调用 AbortController.abort() 去中止该请求
   signal: controller.signal
}).then(function(response) {
   //...
});
// 中断
controller.abort()
`AbortController` 是一个简单的对象,当 `abort()` 方法被调用时,会在自身的 `signal` 属性上生成 `abort` 事件(并将 `signal.aborted` 设置为 `true`)

与axios封装到一起的写法如下只是对前文提到的requestCancel.ljs文件稍作修改:

js 复制代码
export default class CancelRequest {
    constructor() {
        this.pendingRequest = new Map()
    }
    // 根据请求信息生成唯一标识key
    geterateReqKey(config) {
        const { url, method } = config
        // console.log(url, method, 'eeeee')
        return [url, method].join('&')
    }
    // 把当前请求信息添加到pendingRequest对象中
    addPendingRequest(config) {
        const requestKey = this.geterateReqKey(config)
        const controller = new AbortController()
        config.signal = controller.signal
        console.log(config, 'study')

        if (!this.pendingRequest.has(requestKey)) {
            this.pendingRequest.set(requestKey, controller)
        }
    }
    // 检查是否存在重复请求,若存在则取消前一次请求
    removePendingRequest(config) {
        const requestKey = this.geterateReqKey(config)
        if (this.pendingRequest.has(requestKey)) {
            const abortContorller = this.pendingRequest.get(requestKey)
            if (abortContorller) {
                abortContorller.abort()
            }
            // 删除map中对应的属性
            this.removeRequestKey(config)
        }
    }
    // 从pendingRequest中删除对应的key
    removeRequestKey(config) {
        const requestKey = this.geterateReqKey(config)
        this.pendingRequest.delete(requestKey)
    }
    // 合并 清哥写的 最后50个请求的撤销方法
    push(canceller) {
        const len = this.cacheRequestList.length
        if (len === this.max) {
            this.cacheRequestList = this.cacheRequestList.slice(len - (this.max - 1))
        }
        this.cacheRequestList.push(canceller)
    }
    // 中止发出的请求
    cancelAllRequest() {
        for (let i = 0; i < this.cacheRequestList.length; i++) {
            const canceller = this.cacheRequestList[i]
            canceller('登录过期,中止请求')
        }
        // 清空所有保存的请求撤销函数
        this.cacheRequestList = []
    }
}
相关推荐
程序员爱钓鱼2 小时前
Go语言实战案例 — 项目实战篇:简易博客系统(支持评论)
前端·后端·go
excel9 小时前
ES6 中函数的双重调用方式:fn() 与 fn\...``
前端
可乐爱宅着9 小时前
全栈框架next.js入手指南
前端·next.js
你的人类朋友11 小时前
什么是API签名?
前端·后端·安全
会豪13 小时前
Electron-Vite (一)快速构建桌面应用
前端
中微子13 小时前
React 执行阶段与渲染机制详解(基于 React 18+ 官方文档)
前端
唐某人丶13 小时前
教你如何用 JS 实现 Agent 系统(2)—— 开发 ReAct 版本的“深度搜索”
前端·人工智能·aigc
中微子13 小时前
深入剖析 useState产生的 setState的完整执行流程
前端
遂心_13 小时前
JavaScript 函数参数传递机制:一道经典面试题解析
前端·javascript
小徐_233313 小时前
uni-app vue3 也能使用 Echarts?Wot Starter 是这样做的!
前端·uni-app·echarts