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 = []
    }
}
相关推荐
TimelessHaze10 分钟前
拆解字节面试题:async/await 到底是什么?底层实现 + 最佳实践全解析
前端·javascript·trae
执键行天涯40 分钟前
从双重检查锁定的设计意图、锁的作用、第一次检查提升性能的原理三个角度,详细拆解单例模式的逻辑
java·前端·github
青青子衿越42 分钟前
微信小程序web-view嵌套H5,小程序与H5通信
前端·微信小程序·小程序
OpenTiny社区1 小时前
TinyEngine 2.8版本正式发布:AI能力、区块管理、Docker部署一键强化,迈向智能时代!
前端·vue.js·低代码
qfZYG1 小时前
Trae 编辑器在 Python 环境缺少 Pylance,怎么解决
前端·vue.js·编辑器
bug爱好者1 小时前
Vue3 基于Element Plus 的el-input,封装一个数字输入框组件
前端·javascript
Silence_xl1 小时前
RACSignal实现原理
前端
柯南二号1 小时前
【大前端】实现一个前端埋点SDK,并封装成NPM包
前端·arcgis·npm
dangkei1 小时前
【Wrangler(Cloudflare 的官方 CLI)和 npm/npx 的区别一次讲清】
前端·jvm·npm
乔公子搬砖1 小时前
小程序开发提效:npm支持、Vant Weapp组件库与API Promise化(八)
前端·javascript·微信小程序·js·promise·vagrant·事件绑定