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 = []
    }
}
相关推荐
Pedantic29 分钟前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘1 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆1 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师2 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆2 小时前
VSCode自动格式化三要素
前端
爱勇宝3 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen3 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518136 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode6 小时前
Redis 在生产项目的使用
前端·后端