一般使用到canceltoken应该是业务上的逻辑需要。例如这样一个场景:切换tab或者点解按钮查看不同的数据详情时,请求的是同一个接口,点击A后立即点击B,如果A的请求返回比B的慢,就会导致页面渲染的是A的数据。
以前也使用过canceltoken,这次再次使用,记录下
- 引用api
...
import axios from 'axios'
...
const instance = axios.create({
...
})
const { CancelToken } = axios
instance.interceptors.request.use(requestInterceptors.bind(this, CancelToken), requestError)
instance.interceptors.response.use(responseInterceptors, responseError)
- 新建cancelHttpReauest.js
// 请求连接的缓存
export let httpRequestList = []
/**
* 取消方法
* @param {Array,String} cancelPath 取消的url集合
* @param {String} errorMsg 标识
*/
export const clearHttpRequestingList = (cancelPath = [], errorMsg = 'cancel') => {
if (Array.isArray(cancelPath)) cancelPath = [...new Set(cancelPath)]
if (httpRequestList.length > 0) {
httpRequestList.forEach((item) => {
const { url, cancel } = item
// url集合
if (Array.isArray(cancelPath) && cancelPath.includes(url)) {
cancel(errorMsg)
item.url = ''
}
// all 取消全部
if (cancelPath === 'all') {
cancel(errorMsg)
item.url = ''
}
})
httpRequestList = httpRequestList.filter((o) => o.url)
}
}
- 在拦截器配置文件中引入cancelHttpReauest.js
...
import { httpRequestList, clearHttpRequestingList } from '../cancelHttpReauest'
...
// 请求拦截器
export function requestInterceptors(CancelToken, config) {
const { url, cancelPath = [] } = config
// 默认取消相同url且正在pending的请求
if (typeof cancelPath !== 'string' && Array.isArray(cancelPath)) cancelPath.push(url)
clearHttpRequestingList(cancelPath)
config.cancelToken = new CancelToken(function executor(cancel) {
httpRequestList.push({ url, cancel }) // 存储cancle
})
...
return config
}
- 调用
// cancelPath不传就取消当前url且正在pending的请求
httpRequest(params,{cancelPath:[...]}).then()
发现问题:例如请求httpRequest,请求时页面需要一个加载遮挡loading,在使用cancelToken之前,loading是这样写的
fn() {
const params = {
...
}
this.loading = true
httpRequest(params).then(() => {
...
}).finally(()=>{
this.loading = false
})
}
这样会导致第二次调用httpRequest时,this.loading = true会比第一次this.loading = false先执行。页面效果就是没有加载遮罩了。
将loading封装到axios拦截器中。
封装后的代码
...
import { httpRequestList, clearHttpRequestingList } from '../cancelHttpReauest'
...
// 请求拦截器
export function requestInterceptors(CancelToken, config) {
const { url, cancelPath = [], loading } = config
if (typeof cancelPath !== 'string' && Array.isArray(cancelPath)) cancelPath.push(url)
clearHttpRequestingList(cancelPathWithCurrentPath)
config.cancelToken = new CancelToken(function executor(cancel) {
httpRequestList.push({ url, cancel }) // 存储cancle
})
if (loading) {
const { vm, loadingKey } = loading
vm?.[loadingKey] = true
}
...
return config
}
// 响应拦截器
export function responseInterceptors(response) {
...
const { config } = response
const { loading } = config
if (loading) {
const { vm, loadingKey } = loading
vm?.[loadingKey] = false
}
...
return data
}
调用
// cancelPath不传就取消当前url且正在pending的请求
httpRequest(params,{cancelPath:[...], loading: { vm: this, loadingKey: 'loading' }}).then()