axios取消请求CancelToken的原理解析及用法示例
文章目录
- 一、axios的实例与请求流程
- 二、CancelToken 的作用
- 三、CancelToken 的实现原理
- 四、取消请求的流程
- 五、CancelToken用法
- 六、利用拦截器取消请求
- 1、axios请求拦截器
- 2、axios响应拦截器
- 3、利用路由导航守卫取消请求
一、axios的实例与请求流程
下图是axios实例属性的简图。
下图是axios的请求流程
axios的实例上,其实主要就这三个东西:
- config:配置,比如url、method、params、headers等等
- interceptors :拦截器,分为请求拦截器和返回拦截器。
- request:调用xhr或者http请求的方法,参数就是config
二、CancelToken 的作用
Axios 是一个基于 Promise 的 HTTP 客户端,用于在浏览器和 Node.js 中发起 HTTP 请求。除了强大的功能和丰富的配置选项,Axios 还提供了一个实用的特性——CancelToken,它允许我们取消一个或多个正在进行的请求。
在网络应用中,我们经常需要处理一些可能需要取消的请求,比如用户在加载数据时跳转到了其他页面,或者在表单提交前用户点击了取消按钮。在这些场景下,如果能够取消请求,就能避免不必要的资源消耗和更好的用户体验。
三、CancelToken 的实现原理
CancelToken 的工作原理依赖于 Promise 的链式调用和 throw 语句。在 Axios 发起请求时,会检查请求的 cancelToken 属性,如果存在,就将其添加到当前请求的 cancelToken 列表中。
当调用 cancel 方法时,CancelToken 会抛出一个带有特定 message 的 Cancel 对象。这个对象会被 Promise 的 catch 方法捕获,从而触发请求的取消逻辑。
四、取消请求的流程
CancelToken的静态方法CancelToken.source返回了两个东西:分别是token和cancel方法。
下图是取消请求的流程
可以看到,流程里,axios分别在请求前、请求中、请求后进行了是否取消的判断。
这也就意味着,当我们在请求拦截器中调用cancel方法,axios会识别到,抛出异常。
五、CancelToken用法
先引用axios.CancelToken,然后调用source()方法,会产生一个token和cancel
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
// get请求示例
axios.get('/user/12345', {
cancelToken: source.token
}).catch((thrown)=> {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
// post请求示例
axios.post('/user/12345', {name: 'new name'}, {cancelToken: source.token});
// 取消请求(message 参数是可选的)
source.cancel('不想请求了');
- get请求的时候,cancelToken是放在第二个参数里;
- post的时候,cancelToken是放在第三个参数里。
六、利用拦截器取消请求
先封装一下方法
// 全局变量对象,专门存放所有请求的cancel方法
window.pendingRequest = new Map()
// 生成request的唯一的标识
const generateRequestKey = (config = {}) => {
// 通过url,method,params,data生成唯一key,用于判断是否重复请求
// params为get请求参数,data为post请求参数
const {url,method,params,data} = config
if (method == 'get') {
return [url, method, qs.stringify(params)].join('&')
}
return [url, method, 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 cancelRepeatRequest = (config) => {
const key = generateRequestKey(config)
if (pendingRequest.has(key)) {
const cancelToken = pendingRequest.get(key)
cancelToken(key) // 取消之前发送的请求
pendingRequest.delete(key) // 请求对象中删除requestKey
}
}
1、axios请求拦截器
在发送请求之前,为每个请求生成一个唯一的标识,这个标识可以根据请求的URL、方法以及所传递的参数等信息生成。
通过使用Axios的请求拦截器,你可以在请求发送前进行检查,如果发现有一个相同标识的请求已经在进行中,就可以使用CancelToken取消前一个请求,或者跳过当前请求。
// 创建axios实例
const httpService = axios.create({});
// request拦截器
httpService.interceptors.request.use(
config => {
// 取消重复请求
cancelRepeatRequest(config)
// 添加请求到队列
addPendingRequest(config)
return config;
},
error => {
return Promise.reject(error);
}
)
2、axios响应拦截器
响应拦截器可以用来清理已完成或已取消的请求的记录,确保请求映射表只保留活跃的请求。这个机制使得在实际应用中管理和取消重复的请求变得容易。
httpService.interceptors.response.use(
response => {
// 取消请求队列中该请求
cancelRepeatRequest(response .config);
if (response.status == 200) {
return response;
} else {
return Promise.reject(response);
}
},
// 处理处理
error => {
// 取消请求队列中该请求
cancelRepeatRequest(error.config || {})
return Promise.reject(error);
}
)
这里我同时发送了3次重复的history请求,过滤取消了2次,成功发送了1次。
3、利用路由导航守卫取消请求
当路由切换的时候,在路由钩子里遍历这个数组,调用所有的cancel方法,将上一个页面还未完的请求取消,这样就可以加快页面的加载,避免不必要的等待。
router.beforeEach((to, from, next) => {
if(pendingRequest){
pendingRequest.forEach((cancel) => {
cancel();
});
pendingRequest = new Map();
}
next();
})
当路由切换页面的时候,遍历全局数组,将上一个页面的所有请求cancel掉。
图中history接口是上个页面的,还没返回结果,切换路由后,请求被取消。