DevNow 是一个精简的开源技术博客项目模版,支持 Vercel 一键部署,支持评论、搜索等功能,欢迎大家体验。
前言
最近面试中有一个问题出现的频率相对高一些,就是 如果有并发请求的时候,如果中断之前的请求?
:::tips[讨论]
这个问题的出发点:
- 同一个异步请求,如何确保请求的顺序,如果后一个先返回,前一个再返回时,还需要覆盖之前的数据吗。
- 多个异步请求,如果有新的请求发出是,之前没有响应的请求是否需要中断。
当然这个问题不单单是指请求,通过 Promise
封装的异步方法都在讨论的范围中。
:::
目前比较好的一种实现方案是基于 AbortController
Api 来实现事件的中断。
1. 什么是 AbortController?
以下是 MDN 关于 AbortController 的描述:
AbortController
接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求 或者事件。
你可以使用 AbortController()
构造函数创建一个新的 AbortController
对象。使用 AbortSignal
对象可以完成与异步操作的通信。
可以看到 AbortController
是 JavaScript
中用于中断任何事的全局类。使用方法如下:
const controller = new AbortController();
controller.signal;
controller.abort();
一旦创建一个 AbortController
实例,就会得到两件事:
signal
属性,它是 AbortSignal 的一个实例。它可以填充到提供响应中断事件的 API 中,然后执行中断。举例来说,填充到 fetch 请求中可以中断请求;.abort()
方法,该方法一旦执行,就会触发signal
上的中断事件。
到目前为止一切顺利。但实际的中止逻辑在哪里呢?这正是它的美妙之处 —— 由使用者定义。中止处理的核心在于监听中止事件,并以适合当前逻辑的方式实现中止:
controller.signal.addEventListener('abort', () => {
// Implement the abort logic.
});
下面我们探究在 JavaScript
标准中提供的 AbortSignal
。
2. 使用
2.1 事件监听
可以在事件监听中添加中断属性 signal
,当中断发生时它会自动移除事件监听。
const controller = new AbortController();
window.addEventListener('resize', listener, {
signal: controller.signal });
controller.abort();
执行 controller.abort()
会移除 window
上的 resize
事件监听。这是非常优雅的卸载事件监听方式,因为不再只能使用固定的卸载事件监听函数 .removeEventListener()
。
// 1
function listener() {
}
window.addEventListener('resize', listener);
window.removeEventListener('resize', listener);
// 2
const controller = new AbortController();
window.addEventListener('resize', () => {
}, {
signal