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 = []
    }
}
相关推荐
sniper_fandc19 小时前
Axios快速上手
vue.js·axios
哟哟耶耶19 小时前
Starting again-02
开发语言·前端·javascript
Apifox.19 小时前
Apifox 9 月更新| AI 生成接口测试用例、在线文档调试能力全面升级、内置更多 HTTP 状态码、支持将目录转换为模块
前端·人工智能·后端·http·ai·测试用例·postman
Kitasan Burakku19 小时前
Typescript return type
前端·javascript·typescript
叁佰万19 小时前
前端实战开发(一):从参数优化到布局通信的全流程解决方案
前端
笔尖的记忆19 小时前
js异步任务你都知道了吗?
前端·面试
光影少年19 小时前
react生态
前端·react.js·前端框架
golang学习记20 小时前
从0死磕全栈之Next.js 中的错误处理机制详解(App Router)
前端
力Mer20 小时前
console.log()控制台异步打印与对象展开后不一致问题
前端·javascript
WillaWang20 小时前
Liquid:在assign定义变量时使用allow_false
前端