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获取单选按钮的数据
Nov 27 Javascript
修改发贴的编辑功能
Mar 07 Javascript
JSON无限折叠菜单编写实例
Dec 16 Javascript
jquery 按键盘上的enter事件
May 11 Javascript
JS控制FileUpload的上传文件类型实例代码
Oct 07 Javascript
深入学习Bootstrap表单
Dec 13 Javascript
详解vue + vuex + directives实现权限按钮的思路
Oct 24 Javascript
微信小程序实现点击按钮修改view标签背景颜色功能示例【附demo源码下载】
Dec 06 Javascript
vue+element创建动态的form表单及动态生成表格的行和列
May 20 Javascript
微信小程序实现侧边分类栏
Oct 21 Javascript
Vue 实现复制功能,不需要任何结构内容直接复制方式
Nov 09 Javascript
vue-treeselect的基本用法以及解决点击无法出现拉下菜单
Apr 30 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中使用__autoload()自动加载未定义类的实现代码
2013/02/06 PHP
md5 16位二进制与32位字符串相互转换示例
2013/12/30 PHP
叫你如何修改Nginx与PHP的文件上传大小限制
2014/09/10 PHP
php无法连接mysql数据库的正确解决方法
2016/07/01 PHP
基于jquery的仿百度的鼠标移入图片抖动效果
2010/09/17 Javascript
浏览器常用高宽的jquery插件
2011/02/24 Javascript
easyui datagrid 键盘上下控制选中行示例
2014/03/31 Javascript
javascript 小数取整简单实现方式
2014/05/30 Javascript
js获取元素外链样式的方法
2015/01/27 Javascript
js实现带关闭按钮始终显示在网页最底部工具条的方法
2015/03/02 Javascript
JS实现的在线调色板实例(附demo源码下载)
2016/03/01 Javascript
Javascript中apply、call、bind的巧妙使用
2016/08/18 Javascript
JS常用知识点整理
2017/01/21 Javascript
vue实现app页面切换动画效果实例
2017/05/23 Javascript
angularJS的radio实现单项二选一的使用方法
2018/02/28 Javascript
webpack+react+antd脚手架优化的方法
2018/04/02 Javascript
vuejs前后端数据交互之从后端请求数据的实例
2018/08/11 Javascript
jquery分页优化操作实例分析
2019/08/23 jQuery
React-redux实现小案例(todolist)的过程
2019/09/29 Javascript
python批量添加zabbix Screens的两个脚本分享
2017/01/16 Python
对Pytorch中nn.ModuleList 和 nn.Sequential详解
2019/08/18 Python
Python模拟伯努利试验和二项分布代码实例
2020/05/27 Python
Python os库常用操作代码汇总
2020/11/03 Python
基于Python模拟浏览器发送http请求
2020/11/06 Python
CSS3 mask 遮罩的具体使用方法
2017/11/03 HTML / CSS
Sandro Paris美国官网:典雅别致的法国时尚服饰品牌
2017/12/26 全球购物
Stella McCartney官网:成衣、包袋、香水、内衣、童装及Adidas系列
2018/12/20 全球购物
Moda Operandi官网:美国奢侈品电商,海淘秀场T台同款
2020/05/26 全球购物
药学专业大学生自荐信
2013/09/28 职场文书
旅游局领导班子“四风”问题对照检查材料思想汇报
2014/09/29 职场文书
西安大雁塔导游词
2015/02/10 职场文书
奖金申请报告模板
2015/05/15 职场文书
烛光里的微笑观后感
2015/06/17 职场文书
2016预备党员培训心得体会
2016/01/08 职场文书
golang连接MySQl使用sqlx库
2022/04/14 Golang
优化Mysql查询的示例
2022/04/26 MySQL