一、我们为什么需要取消?
我们常见的业务场景有,用户频繁点击数据提交,用户频繁的切换数据量大的表格,在网络较差的环境下,会出现一些异常的情况,当然,各类UI库提供的loading状态可以帮我们解决以上烦恼,以及可以通过防抖解决部分问题,但是我们今天从另一个方面,使用axios的cancelToken取消上次重复的请求。
二、我们怎样取消?
先看一下aixos官方文档的cancelToken的代码
方法1:可以使用 CancelToken.source
工厂方法创建 cancel token,像这样:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
方法2:还可以通过传递一个 executor 函数到 CancelToken
的构造函数来创建 cancel token:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// cancel the request
cancel();
如何在我们项目中使用呢?
我们项目中,更常用的做法是在拦截器interceptors
中使用。
当然,我们需要先定义一个map
对象用于存储相关值(map为es6新增对象,如果你对es6不了解,请查阅阮一峰的ECMAScript 6 入门
// 用于存储pending的请求(处理多条相同请求)
const pendingRequest = new Map()
其次,需要封装一些辅助类的函数
- generateRequestKey 将重复的请求生成一个唯一的key
import qs from 'qs'
// 生成request的唯一key
const generateRequestKey = (config = {}) => {
// 通过url,method,params,data生成唯一key,用于判断是否重复请求
// params为get请求参数,data为post请求参数
const { url, method, params, data } = config
return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
}
- addPendingRequest将重复的请求,保存到到
pendingRequest
中
// addPendingRequest 将重复请求添加到pendingRequest中
const addPendingRequest = (config) => {
const key = generateRequestKey(config)
if (!pendingRequest.has(key)) { //判断pendingRequest中是否存在key
config.cancelToken = new axios.CancelToken(cancel => {
pendingRequest.set(key, cancel) //将key和cancel以键值对的方式保存
})
}
}
- 将重复的请求取消,并在
pendingRequest
中删除
// removePendingRequest 取消重复请求
const removePendingRequest = (config) => {
const key = generateRequestKey(config)
if (pendingRequest.has(key)) { //判断pendingRequest中是否存在key
const cancelToken = pendingRequest.get(key)
cancelToken(key) // 取消之前发送的请求
pendingRequest.delete(key)// 请求对象中删除requestKey
}
}
完整代码
import axios from 'axios'
import qs from 'qs'
// 用于存储pending的请求(处理多条相同请求)
const pendingRequest = new Map()
// 生成request的唯一key
const generateRequestKey = (config = {}) => {
// 通过url,method,params,data生成唯一key,用于判断是否重复请求
// params为get请求参数,data为post请求参数
const { url, method, params, data } = config
return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
}
// 将重复请求添加到pendingRequest中
const addPendingRequest = (config) => {
const key = generateRequestKey(config)
if (!pendingRequest.has(key)) {
config.cancelToken = new axios.CancelToken(cancel => {
pendingRequest.set(key, cancel)
})
}
}
// 取消重复请求
const removePendingRequest = (config) => {
const key = generateRequestKey(config)
if (pendingRequest.has(key)) {
const cancelToken = pendingRequest.get(key)
cancelToken(key) // 取消之前发送的请求
pendingRequest.delete(key)// 请求对象中删除requestKey
}
}
// 请求拦截器
service.interceptors.request.use(
config => {
// 处理重复请求
removePendingRequest(config)
addPendingRequest(config)
return config
},
error => {
// 处理请求错误
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
// 移除重复请求
removePendingRequest(response.config)
return res
},
error => {
// 异常情况console,方便排查问题
console.log('error', error)
// 移除重复请求
removePendingRequest(error.config || {})
return Promise.reject(error)
}
)
三、结尾
代码中如有需要更正和修改的地方,欢迎评论留言,我也会积极回复。