前言
在真实的项目中其实很多时候都有接口请求已经发出,但还没响应完成就要取消请求的业务场景。
有可能这些场景我们不做处理也不影响功能正常使用,但如果做接口终止处理客户体验会更好并且也节省了服务器资源,如下几个常见的业务场景:
(1)页面快速切换时: 页面初始化时请求的接口没必要继续请求应当终止
(2)表格分布切换时: 用户快速切换分页会重复请求相同接口,这时就当终止前面请求
(3)文件上传时: 如果是大文件持续上传,就当有终止上传按钮
(4)连续快速触发请求: 例如在搜索输入框中动态获取内容,输入太快就终止前面的只保留最后一次请求
(5)超时处理: 例如设置超过
30S
还没有响应,就当终止请求,提示请求超时(6)滚动加载: 如果滚动加载图片时用户滚动的超级快,那么就当只请求视口上下的内容,其它区域终止请求
如何取消请求?
主流取消接口请求的个人推荐方法有两种分别是AbortController
和CancelToken
。这两种方法各有优劣,具体对比如下。
兼容性: AbortController
不支持IE
浏览器,因此CancelToken
兼容性更好。
规范化: AbortController
是W3C
标准的一部分,而CancelToken
是主流库存axios
的一部分。
集成性: AbortController
可以非常方便地和各种主流前端框架结合使用更加灵活,而CancelToken
仅限于 axios
库。
控制度: AbortController
能更精细控制操作,例如可以通过 signal
对象去检查取消状态,但CancelToken
就较简单,灵活性相对较低,仅仅提供了基本的取消功能。
综上所述,我们可以根据具体的项目情况去选择合适的工具去处理.
下面我将以Vue
框架(其它框架用法也一样的)为例分别讲解AbortController
和CancelToken
的具体用法。
AbortController具体用法
简述: AbortController
其实是用于取消异步的一个 JavaScript API
。AbortController
允许我们在任何时候都能取消正在进行的异步请求操作,合理使用能避免不必要的资源消耗。
AbortController
用法主要由两个部分组成:
AbortController
:这部分用于创建和管理取消命令。Signal
:用于传递取消命令。
基本用法
下面上一个完整的基本
使用示例
<template>
<div>
<button @click="cancel">取消请求</button>
<button @click="getData">请求接口</button>
</div>
</template>
<script setup>
import axios from 'axios';
import { onUnmounted, ref } from 'vue';
// 组件卸载时需要清理控制器,否则容易引起内存泄漏
onUnmounted(() => {
if (myController.value) {
myController.value.abort();
}
});
// 定义控制器
const myController = ref(null);
// 取消请求
const cancel = () => {
if (myController.value) {
myController.value.abort();
console.log('请求已取消');
}
};
// 发送请求
const getData = () => {
// 如果有正在请求,则取消之前的请求
if (myController.value) {
myController.value.abort();
}
// 创建一个新的 AbortController
myController.value = new AbortController();
const signal = myController.value.signal;
// 请求接口
axios.get('/api/test/data', { signal })
.then(res => {
// 响应数据
})
.catch(error => {
if (error.name === 'AbortError') {
// 请求被取消
} else {
// 错误
}
});
};
</script>
代码解释:
创建一个myController
用于存储 AbortController
实例引用变量。点击取消请求或者有新的请求时,如果 myController
存在,则调用 abort()
方法取消请求。取消之后创建一个新的 AbortController
实例,并且获取 signal
作为请求的取消信号。
注意: 组件卸载时记得要在onUnmounted
生命周期中清理控制器,避免出现内存泄漏。
进阶用法
上面是针对业务中某个小场景基本使用方法,我们可以结合拦截器进行封装。
具体思路:对每个请求都用
Map
把AbortController
和url
以键值对的形式保存起来,只要经过请求拦截器就判断当前url
是否正在请求中,如果是就移除正在请求的url
然后重新执行。
以下提供思路,如果在真实项目要结合拦截器封装。
const pendingMap = new Map();
// 添加请求 函数,把正在请求的url保存起来
function addPending(config) {
this.removePending(config);
const url = getPendingUrl(config);
const controller = new AbortController();
config.signal = config.signal || controller.signal;
if (!pendingMap.has(url)) {
// 如果当前请求不在等待中,将其添加到等待中
pendingMap.set(url, controller);
}
}
// 移除请求 函数
function removePending(config) {
const url = getPendingUrl(config);
if (pendingMap.has(url)) {
const controller = pendingMap.get(url);
controller.abort();
pendingMap.delete(url);
}
}
function getPendingUrl(config) {
return [config.method, config.url, JSON.stringify(config.params), JSON.stringify(config.data)].join('&');
}
解释: 用addPending
添加请求之前,先调用removePending
把可能要终止的请求进行终止,然后把AbortController
和url
以键值对的形式保存起来。
CancelToken具体用法
简述: CancelToken
是 Axios
库本身提供的用于取消请求的一个方法,局限于Axios
库。CancelToken
不需要手动处理 AbortController
可以更方便地管理接口请求的取消。
<template>
<div>
<button @click="myCancel">取消请求</button>
<button @click="getData">请求接口</button>
</div>
</template>
<script setup>
import axios from 'axios';
import { onUnmounted, ref } from 'vue';
// 组件卸载时需要清理控制器,否则容易引起内存泄漏
onUnmounted(() => {
if (myCancelSource.value) {
myCancelSource.value.cancel('组件卸载');
}
});
// 定义控制器
const myCancelSource = ref(null);
// 取消请求
const myCancel = () => {
if (myCancelSource.value) {
myCancelSource.value.cancel('请求被取消');
}
};
// 发送请求
const getData = () => {
// 如果有正在请求,则取消之前的请求
if (myCancelSource.value) {
myCancelSource.value.cancel('请求被取消');
}
// 创建一个新的 CancelToken
myCancelSource.value = axios.CancelToken.source();
// 请求接口
axios.get('/api/test/data', { cancelToken: myCancelSource.value.token })
.then(res => {
// 响应数据
})
.catch(error => {
if (axios.isCancel(error)) {
// 请求被取消
} else {
// 错误
}
});
};
</script>
代码解释: 用法跟上面的AbortController
有点类似,先创建一个引用变量myCancelSource
,每次进行请求就先判断是否正在请求,如果是就用.cancel
终止,然后用axios.CancelToken.source()
创建一个新的 CancelToken
。
小结
其实我个人更推荐
AbortController
,其一是因为AbortController
更规范并且更灵活好用适用范围更广,其二是因为就算CancelToken.source()
了,也是会对后端产生了一次完整请求,只是前端不等待返回内容处理而已,后端资源压力还是给到了。
除了上面列出的业务场景外肯定还有很多其它应用场景适用,有时甚至和防抖节流应用场景比较接近,具体就要看对应的项目灵活选择解决方案了。
好啦先写到这里,如果哪里写的不对或者有好的建议欢迎大佬们指出。