当前位置: 代码迷 >> 综合 >> axios cancel repeated functions
  详细解决方案

axios cancel repeated functions

热度:5   发布时间:2023-12-25 17:39:45.0

axios如何取消重复请求

javascript axios es6
阅读约 9 分钟
在开发中,经常会遇到接口重复请求导致的各种问题。
对于重复的get请求,会导致页面更新多次,发生页面抖动的现象,影响用户体验。
对于重复的post请求,会导致在服务端生成两次记录(例如生成两条订单记录)。
如果当前页面请求还未响应完成,就切换到了下一个路由,那么这些请求直到响应返回才会中止。
无论从用户体验或者从业务严谨方面来说,取消无用的请求确实是需要避免的。
当然我们可以通过页面loading来避免用户进行下一次的操作,但本文只讨论单纯的如何取消这些无用的请求。

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

1通过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.');

2通过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 parametercancel = 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 requestreturn 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()
})
  相关解决方案