相关文章推荐

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();
    

    取消定时器 - 高级版:

    需要 Node.js v16 的支持

    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

    前端 @ TikTok
    粉丝
     
    推荐文章