前言
AbortController 是一个控制器对象,你可以通过 new 构造函数的形式返回一个 AbortSignal 对象:
const controller = new AbortController();
- 它有一个方法
abort()
,用于触发abort
事件 - 和单个属性
signal
,可以在该属性上设置abort
事件监听
controller.signal.onabort = () => {};
controller.signal.addEventListener("abort", () => {});
当 controller.abort()
被调用时:
controller.signal
就会触发abort
事件controller.signal.aborted
属性从false
变为true
了解前置知识后,看一下实际应用的场景。
取消 Fetch 请求
在没有 Fetch API 之前,一般通过 XMLHttpRequest
来实现数据更新,当我们发出请求后想临时取消,可以调用 XMLHttpRequest.abort()
方法来终止该请求。
有了 AbortController,同样可以实现取消 Fetch 请求。
const controller = new AbortController();
const signal = controller.signal;
fetch("/", { signal }).catch(err => {
console.log(err); // DOMException: The user aborted a request.
console.log("aborted:", signal.aborted); // aborted: true
});
controller.abort();
移除事件监听
以往,我们通过 EventTarget.addEventListener()
来为一个 DOM、document 或 window 添加事件监听。
它的函数签名如下:
target.addEventListener(type, listener);
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);
options 是一个可选参数对象,它包含了以下属性来决定事件触发的行为:
capture: boolean
,如果是 true,表示 listener 会在指定 type 事件类型的 捕获 阶段触发。false 则为 冒泡 阶段once: boolean
,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除passive: boolean
,设置为 true 时,表示 listener 永远不会调用 preventDefault(),用于优化页面的滚动性能
当不再需要事件监听时,调用 EventTarget.removeEventListener()
来移除事件监听,但要保证 参数和添加监听时是一致的,即 type
事件类型必须是同一字符串,listener
回调函数必须是同一个函数引用,options
对象必须属性相同。
const btn = document.getElementsByTagName("button")[0];
btn.addEventListener("click", () => console.log("button clicked"), {
capture: true,
});
// Doesn't work, because has different callback functions
btn.removeEventListener("click", () => console.log("button clicked"), {
capture: true,
});
在 Chrome 88 发布时,添加了一个 AbortSignal in addEventListener 的新特性,它允许 AbortController 实例的 signal
属性传入 addEventListener()
的 options 对象,一旦调用实例的 abort()
方法,就会移除对应的事件监听。
这意味着开发者摆脱了调用 removeEventListener()
时繁琐的参数困境,只需使用 abort()
方法,既减少了代码量,又一目了然。
下面的代码实现了 "click once" 的效果,实测(Chrome 90)运行成功。
const controller = new AbortController();
const signal = controller.signal;
const btn = document.getElementsByTagName("button")[0];
const handle = () => {
console.log("button clicked");
controller.abort();
};
btn.addEventListener("click", handle, {
capture: true,
signal,
});
// equals to
// btn.addEventListener("click", handle, { once: true });
其他应用场景
取消定时器 - 初级版:
function timeout(duration, signal) {
return new Promise((resolve, reject) => {
const handle = setTimeout(resolve, duration);
signal.onabort = () => {
clearTimeout(handle);
reject(new Error("The timeout was aborted"));
};
});
}
const controller = new AbortController();
timeout(10000, controller.signal).catch(err => console.log(err));
controller.abort();
取消定时器 - 高级版:
import { setTimeout } from "timers/promises";
const ac = new AbortController();
const signal = ac.signal;
setTimeout(1000, "foobar", { signal })
.then(console.log)
.catch(err => {
if (err.name === "AbortError") console.log("The timeout was aborted");
});
ac.abort();
Reference
-
MDN - EventTarget.addEventListener()
-
CSS - TRICKS - Using AbortController as an Alternative for Removing Event Listeners
-
WHATWG living standard - AbortController
-
nodejs - Cancelling timers
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!