axios如何取消重复无用的请求详解


Posted in Javascript onDecember 15, 2019

前言

在开发中,经常会遇到接口重复请求导致的各种问题。

对于重复的get请求,会导致页面更新多次,发生页面抖动的现象,影响用户体验。

对于重复的post请求,会导致在服务端生成两次记录(例如生成两条订单记录)。

如果当前页面请求还未响应完成,就切换到了下一个路由,那么这些请求直到响应返回才会中止。

无论从用户体验或者从业务严谨方面来说,取消无用的请求确实是需要避免的。

当然我们可以通过页面loading来避免用户进行下一次的操作,但本文只讨论单纯的如何取消这些无用的请求。

axios 的 cancelToken

axios是一个主流的http请求库,它提供了两种取消请求的方式。

通过axios.CancelToken.source生成取消令牌token和取消方法cancel

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 {
 // handle error
 }
});

axios.post('/user/12345', {
 name: 'new name'
}, {
 cancelToken: source.token
})

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');

通过axios.CancelToken构造函数生成取消函数

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
 cancelToken: new CancelToken(function executor(c) {
 // An executor function receives a cancel function as a parameter
 cancel = c;
 })
});

// cancel the request
cancel();

需要注意的是在catch中捕获异常时,应该使用axios.isCancel()判断当前请求是否是主动取消的,以此来区分普通的异常逻辑。

封装取消请求逻辑

上面有两种取消请求,用哪种都是可以的,这里使用第二种。

取消请求主要有两个场景:

  • 当请求方式method,请求路径url,请求参数(get为params,post为data)都相同时,可以视为同一个请求发送了多次,需要取消之前的请求
  • 当路由切换时,需要取消上个路由中未完成的请求

我们封装几个方法:

// 声明一个 Map 用于存储每个请求的标识 和 取消函数
const pending = new Map()
/**
 * 添加请求
 * @param {Object} config 
 */
const addPending = (config) => {
 const url = [
 config.method,
 config.url,
 qs.stringify(config.params),
 qs.stringify(config.data)
 ].join('&')
 config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
 if (!pending.has(url)) { // 如果 pending 中不存在当前请求,则添加进去
  pending.set(url, cancel)
 }
 })
}
/**
 * 移除请求
 * @param {Object} config 
 */
const removePending = (config) => {
 const url = [
 config.method,
 config.url,
 qs.stringify(config.params),
 qs.stringify(config.data)
 ].join('&')
 if (pending.has(url)) { // 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
 const cancel = pending.get(url)
 cancel(url)
 pending.delete(url)
 }
}
/**
 * 清空 pending 中的请求(在路由跳转时调用)
 */
export const clearPending = () => {
 for (const [url, cancel] of pending) {
 cancel(url)
 }
 pending.clear()
}

Map是ES6中一种新型的数据结构,本身提供了诸多方法,方便操作,适合当前场景。如果不熟悉的可以查看ECMAScript 6 入门。

在给config.cancelToken赋值的时候,需要判断当前请求是否已经在业务代码中使用了cancelToken

qs是一个专门用来转换对象和字符串参数的库,最初是由 TJ 创建并维护的,也是axios推荐使用的参数序列化库。这里我们的目的只是单纯的将参数对象转换为字符串方便拼接。

Map结构默认部署了Symbol.iterator属性,可以使用for...of循环直接获取键名和键值,当然你也可以使用for...in循环。

在 axios 拦截器中使用

主要的方法已经写好了,只需要添加到axios拦截器中就可以了。

axios.interceptors.request.use(config => {
 removePending(options) // 在请求开始前,对之前的请求做检查取消操作
 addPending(options) // 将当前请求添加到 pending 中
 // other code before request
 return config
}, error => {
 return Promise.reject(error)
})

axios.interceptors.response.use(response => {
 removePending(response) // 在请求结束后,移除本次请求
 return response
}, error => {
 if (axios.isCancel(error)) {
 console.log('repeated request: ' + error.message)
 } else {
 // handle error code
 }
 return Promise.reject(error)
})

将clearPending()方法添加到vue路由钩子函数中

router.beforeEach((to, from, next) => {
 clearPending()
 // ...
 next()
})

测试效果

最后我们可以在浏览器中测试下,可以将chrome中控制面板的Network的网络状态切换为Slow 3G来模拟网速慢的情况。

我们把查询按钮的loading或者disabled属性干掉来方便测试

axios如何取消重复无用的请求详解

在上面控制面板中可以看到,红色的status为canceled的就是被取消的请求。

上面代码在e-admin-vue(一个使用 vue + element-ui + vue-cli3 构建的 rbac 权限模型)或者e-admin-react(一个使用 react + antd + create-react-app 构建的 rbac 权限模型)中都有体现,欢迎 star。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
js 阻止子元素响应父元素的onmouseout事件具体实现
Dec 23 Javascript
JavaScript常用的弹出广告及背投广告实现方法
Feb 06 Javascript
jQuery插件实现文件上传功能(支持拖拽)
Aug 27 Javascript
AngularJS  双向数据绑定详解简单实例
Oct 20 Javascript
浅谈jquery采用attr修改form表单enctype不起作用的问题
Nov 25 Javascript
JS生成随机打乱数组的方法示例
Dec 23 Javascript
js 判断当前时间是否处于某个一个时间段内
Sep 19 Javascript
微信公众号H5之微信分享常见错误和问题(小结)
Nov 14 Javascript
JavaScript内置对象之Array的使用小结
May 12 Javascript
JS数据类型分类及常用判断方法
Nov 19 Javascript
vue实现滚动鼠标滚轮切换页面
Dec 13 Vue.js
vue图片裁剪插件vue-cropper使用方法详解
Dec 16 Vue.js
Vue在chrome44偶现点击子元素事件无法冒泡的解决方法
Dec 15 #Javascript
Angular6项目打包优化的实现方法
Dec 15 #Javascript
vue+elementUI组件table实现前端分页功能
Nov 15 #Javascript
Vue+ElementUI table实现表格分页
Dec 14 #Javascript
微信小程序跨页面传递data数据方法解析
Dec 13 #Javascript
微信小程序swiper使用网络图片不显示问题解决
Dec 13 #Javascript
JS实现移动端双指缩放和旋转方法
Dec 13 #Javascript
You might like
PHP读MYSQL中文乱码的解决方法
2006/12/17 PHP
PHP判断远程图片是否存在的几种方法
2014/05/04 PHP
PHP反射机制原理与用法详解
2017/02/15 PHP
PHP基于简单递归函数求一个数阶乘的方法示例
2017/04/26 PHP
laravel实现查询最后执行的一条sql语句的方法
2019/10/09 PHP
初学prototype,发个JS接受URL参数的代码
2006/09/25 Javascript
Jquery在IE7下无法使用 $.ajax解决方法
2009/11/11 Javascript
AJAX的跨域与JSONP(为文章自动添加短址的功能)
2010/01/17 Javascript
jQuery选择器中含有空格的使用示例及注意事项
2013/08/25 Javascript
判断日期是否能跨月查询的js代码
2014/07/25 Javascript
javascript实现无缝上下滚动特效
2015/12/16 Javascript
JavaScript SweetAlert插件实现超酷消息警告框
2016/01/28 Javascript
JQuery 两种方法解决刚创建的元素遍历不到的问题
2016/04/13 Javascript
jquery实现全选、不选、反选的两种方法
2016/09/06 Javascript
JS简单实现移动端日历功能示例
2016/12/28 Javascript
vue 自定义 select内置组件
2018/04/10 Javascript
python实现列表中由数值查到索引的方法
2018/06/27 Python
python训练数据时打乱训练数据与标签的两种方法小结
2018/11/08 Python
为何人工智能(AI)首选Python?读完这篇文章你就知道了(推荐)
2019/04/06 Python
python实现微信定时每天和女友发送消息
2019/04/29 Python
Python中的pathlib.Path为什么不继承str详解
2019/06/23 Python
Django中的用户身份验证示例详解
2019/08/07 Python
Python ArgumentParse的subparser用法说明
2020/04/20 Python
Python 使用xlwt模块将多行多列数据循环写入excel文档的操作
2020/11/10 Python
移动端适配 使px自动转换rem
2019/08/26 HTML / CSS
html5通过canvas实现刮刮卡效果示例分享
2014/01/27 HTML / CSS
MCM英国官网:奢侈皮具制品
2017/04/18 全球购物
英国和爱尔兰最大的地毯零售商:Kukoon
2018/12/17 全球购物
外贸英语毕业生自荐信
2013/11/14 职场文书
水务局局长岗位职责
2013/11/28 职场文书
建材业务员岗位职责
2013/12/08 职场文书
学生党员公开承诺书
2014/05/28 职场文书
酒店餐厅2014重阳节活动策划方案
2014/09/16 职场文书
四风问题个人对照检查材料
2014/09/26 职场文书
2016年过年放假安排通知
2015/08/18 职场文书
win11电脑关机鼠标灯还亮怎么解决? win11关机后鼠标灯还亮解决方法
2023/01/09 数码科技