CancelToken
CancelToken是axios提供的生成取消请求的函数。 在实战中常见的用法如下:
- 通过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作为第二个参数
- 通过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 = []
}
}