axios-取消重复请求--CancelToken&AbortController

CancelToken

CancelToken是axios提供的生成取消请求的函数。 在实战中常见的用法如下:

  1. 通过axios.CancelToken.source生成取消令牌token和取消方法cancel
js 复制代码
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})
source.cancel('取消操作');

post请求中cancelToken作为第三个参数,get请求中cancel作为第二个参数
  1. 通过axios.CancelToken构造函数生成取消函数
ini 复制代码
let cancel;
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});
cancel();

以上的代码块都是在单个请求方法中操作,接下我们把方法注入封装的axios.

新建requestCancel.js,处理各接口请求时的cancel方法新增与剔除。

js 复制代码
import axios from 'axios'  
export default class CancelRequest {  
constructor() {  
this.pendingRequest = new Map()  
}  
// 根据请求信息生成唯一标识key ,这里认为同一路径,同意请求方式为相同接口 也可控制到参数按需设置  
geterateReqKey(config) {  
const { url, method } = config  
return [url, method].join('&')  
}  
// 把当前请求信息添加到pendingRequest对象中  
addPendingRequest(config) {  
const requestKey = this.geterateReqKey(config)  
config.cancelToken =  
config.cancelToken ||  
new axios.CancelToken((cancel) => {  
if (!this.pendingRequest.has(requestKey)) {  
// 把请求取消方法作为 map 值存起来  
this.pendingRequest.set(requestKey, cancel)  
}  
})  
}  
// 检查是否存在重复请求,若存在则取消前一次请求  
removePendingRequest(config) {  
const requestKey = this.geterateReqKey(config)  
if (this.pendingRequest.has(requestKey)) {  
const cancel = this.pendingRequest.get(requestKey)  
// 取消请求  
cancel(requestKey)  
// 删除map中对应的属性  
this.removeRequestKey(config)  
}  
}  
// 从pendingRequest中删除对应的key  
removeRequestKey(config) {  
const requestKey = this.geterateReqKey(config)  
this.pendingRequest.delete(requestKey)  
}  
}

修改axios 的封装文件

js 复制代码
import RequestCanceller from './requestCanceller'
let requestCanceller = new RequestCanceller()

// 请求拦截处理  
axiosInstance.interceptors.request.use(  
function (config) {  
......  
// 在请求开始之前检查先前的请求,如果是重复请求,删除之前的  
requestCanceller.removePendingRequest(config)  
// 如果不存在 就将当前请求添加到pendingRequest  
requestCanceller.addPendingRequest(config)  
return config  
},  
function (error) {  
return Promise.reject(error)  
}  
)

// 响应拦截  
axiosInstance.interceptors.response.use(  
(response) => {  
// 移除成功的请求记录  
requestCanceller.removeRequestKey(response.config)  
if (response.status === 200) {  
return response.data  
}  
return response  
},  
(error) => {  
if(axios.isCancel(error)){
 console.log('请求取消');
}
// 失败时也需要移除请求记录  
requestCanceller.removeRequestKey(error.config || {})  
//错误码处理  
return permission(error)  
}  
)

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

其实axiosv0.22.0之前取消请求是使用的cancel token API,而在v0.22.0开始 cancel token已经被打上了废弃 的标志 用的是AbortController

AbortController

AbortController是一个DOM API。MDN上对它的介绍是 AbortController接口表示一个控制器对象,允许根据需要终止一个或多个Web请求,使用方法如下:

js 复制代码
//该方法的用法与cancelToken类似

const controller = new AbortController();
 
axios.get('/foo/bar', {
//signal 属性是controller 与 fetch 请求相关联,方便我们通过调用 AbortController.abort() 去中止该请求
   signal: controller.signal
}).then(function(response) {
   //...
});
// 中断
controller.abort()
`AbortController` 是一个简单的对象,当 `abort()` 方法被调用时,会在自身的 `signal` 属性上生成 `abort` 事件(并将 `signal.aborted` 设置为 `true`)

与axios封装到一起的写法如下只是对前文提到的requestCancel.ljs文件稍作修改:

js 复制代码
export default class CancelRequest {
    constructor() {
        this.pendingRequest = new Map()
    }
    // 根据请求信息生成唯一标识key
    geterateReqKey(config) {
        const { url, method } = config
        // console.log(url, method, 'eeeee')
        return [url, method].join('&')
    }
    // 把当前请求信息添加到pendingRequest对象中
    addPendingRequest(config) {
        const requestKey = this.geterateReqKey(config)
        const controller = new AbortController()
        config.signal = controller.signal
        console.log(config, 'study')

        if (!this.pendingRequest.has(requestKey)) {
            this.pendingRequest.set(requestKey, controller)
        }
    }
    // 检查是否存在重复请求,若存在则取消前一次请求
    removePendingRequest(config) {
        const requestKey = this.geterateReqKey(config)
        if (this.pendingRequest.has(requestKey)) {
            const abortContorller = this.pendingRequest.get(requestKey)
            if (abortContorller) {
                abortContorller.abort()
            }
            // 删除map中对应的属性
            this.removeRequestKey(config)
        }
    }
    // 从pendingRequest中删除对应的key
    removeRequestKey(config) {
        const requestKey = this.geterateReqKey(config)
        this.pendingRequest.delete(requestKey)
    }
    // 合并 清哥写的 最后50个请求的撤销方法
    push(canceller) {
        const len = this.cacheRequestList.length
        if (len === this.max) {
            this.cacheRequestList = this.cacheRequestList.slice(len - (this.max - 1))
        }
        this.cacheRequestList.push(canceller)
    }
    // 中止发出的请求
    cancelAllRequest() {
        for (let i = 0; i < this.cacheRequestList.length; i++) {
            const canceller = this.cacheRequestList[i]
            canceller('登录过期,中止请求')
        }
        // 清空所有保存的请求撤销函数
        this.cacheRequestList = []
    }
}
相关推荐
郭老二8 小时前
【前端】vue3:编译步骤
前端
不在逃避q9 小时前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁9 小时前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One9859 小时前
高进度算法模板速记(待完善)
java·前端·算法
陈随易9 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师10 小时前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户0595401744611 小时前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈11 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen12 小时前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒12 小时前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端