前端面试一般喜欢问:

在看来《使用 AbortController 终止 fetch 请求》,觉得写的非常详细,于是提炼下笔记:

AbortController背景介绍

在现在的浏览器中,有两种主要的方法发送请求:XMLHttpRequest 和 fetch。XMLHttpRequest 这个接口在浏览器中存在很长一段时间了,fetch 则是 ES2015 引入的特性。

XMLHttpRequest 可以在请求中途终止(abortable)。举个例子

let xhr = new XMLHttpRequest();
xhr.method = 'GET';
xhr.url = 'https://slowmo.glitch.me/5000';
xhr.open(method, url, true);
xhr.send(); // Abort the request at a later stage
abortButton.addEventListener('click', function() {
  xhr.abort();
});

fetch 刚开始引入时并不支持终止请求。Github 上最早 在 2015 年就有终止 fetch 请求的提案 issue 出现。在 fetch 规范之外也有许多解决这个问题的方案,像 cancelable-promises 和其他 hacks

终于,通用的 AbortController 和 AbortSignal API 出来了。该 API 在 DOM 标准 中定义,而不是在语言规范中定义的。

什么是 AbortController

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

AbortController可以用在fetch和addEventListener,分别用来废弃请求和废弃监听器。

具体看官网:

https://developer.mozilla.org/zh-CN/docs/Web/API/AbortController/AbortController

https://caniuse.com/?search=AbortController

DOM 文档 中有这么一段话:

虽然 Promise 没有提供内置的终止算法(aborting mechanism),但是许多使用它们的 API 需要终止语义。AbortController 提供一个 abort() 方法来支持这些需求,这个方法用来切换相应 AbortSignal 对象的状态。希望支持终止功能的 API 可以接受 AbortSignal 对象,并基于其状态来确定执行流程。

AbortController由两部分构成:abort-signal和abort-controller,架构图如下:

红色部分是我们重点需要关注的部分,因为它们将直接体现在实际应用中

AbortSignal源码解读

AbortSignal源码解析

...
export default class AbortSignal extends EventTarget<Events, EventAttributes> {
    /**
     * 从abortedFlags中获取当前AbortSignal实例aborted状态
     */
    public get aborted(): boolean {
        const aborted = abortedFlags.get(this)
        if (typeof aborted !== "boolean") {
            throw new TypeError(
                `Expected 'this' to be an 'AbortSignal' object, but got ${
                    this === null ? "null" : typeof this
                }`,
            )
        }
        return aborted
    }
}
// 设置abort自定义事件
defineEventAttribute(AbortSignal.prototype, "abort") ... /**
 * 创建一个AbortSinal实例,并设置aborted状态为false,存入abortedFlags中,同时绑定abort事件属性
 */
export function createAbortSignal(): AbortSignal {
    const signal = Object.create(AbortSignal.prototype)
    EventTarget.call(signal)
    abortedFlags.set(signal, false)
    return signal
} /**
 * 设置AbortSinal实例aborted状态为true,同时触发abort监听事件回调
 */
export function abortSignal(signal: AbortSignal): void {
    if (abortedFlags.get(signal) !== false) {
        return
    }     abortedFlags.set(signal, true)
    signal.dispatchEvent<"abort">({ type: "abort" })
}
...
  1. AbortSignal继承EventTarget(第三方依赖包,作用是赋予实例监听自定义事件,它会帮你解决兼容性问题addEventListener/onXX/dispatchEvent),AbortSignal自定义了abort监听事件

  2. AbortSignal.aborted():获取当前实例是否已经启动了abort监听事件。

  3. abortedFlags:map类型,用于存储每个实例的是否已经启动了abort监听事件,默认为false(createAbortSignal创建实例的时候设置),调用abortSignal函数的时候会设置为true

  4. createAbortSignal():构建函数,初始化实例对象为false,绑定abort监听事件(需要用户自己设置abort监听回调事件)

  5. abortSignal(instance):设置当前实例状态为ture,同时触发abort监听回调事件

AbortController源码解析

...
export default class AbortController {
    /**
     * 构造函数,创建一个AbortSignal实例并存入signals中
     */
    public constructor() {
        signals.set(this, createAbortSignal())
    }     /**
     * 从signals中获取当前AbortSignal实例
     */
    public get signal(): AbortSignal {
        return getSignal(this)
    }     /**
     * 先从signals中获取当前AbortSignal实例,然后设置实例aborted状态为true,触发abort监听回调事件
     */
    public abort(): void {
        abortSignal(getSignal(this))
    }
}
...
  1. AbortController构造函数中会调用createAbortSignal创建AbortSignal实例并存入一个Map类型的signals中。

  2. abort()方法会调用abortSignal函数,传入的参数就是从signals中取出来的AbortSignal实例

  3. signal()方法作用是从signals中取出AbortSignal实例

参考源码:

  1. EventTarget源码传送门

  2. AbortController源码传送门

  3. fetch源码传送门

AbortController addEventListener

众所周知,如果需要 removeEventListenr(type, callback), 它的callback必须和addEventListener是同一个函数引用,而在某些业务场景下,我们并不想多写函数可以改成用signal来控制。

例如,当在按钮鼠标时设置一个监听器,在监听器中再监听鼠标移动,鼠标松开关闭监听器:

  document.addEventListener('mousedown', callback);
  document.addEventListener('mouseup', callback2);
  function callback (e) {
      document.addEventListener('mousemove',  callback3);
   }   function callback2 (e) {
     document.removeEventListener('mousemove', callback3);
  }
  
  function callback3(event) {}

如果改写成用AbortController怎么写呢?

    const controller = new AbortController();
    function callback (e) {
      document.addEventListener('mousemove',  (e) => {
          
      },{
           signal: controller.signal  
      });
   }
    document.addEventListener('mousedown', callback);
    document.addEventListener('mouseup', controller.abort);

高级进阶

每次请求,都会重新创建一个AbortSignal实例吗?

答:是的

signals和abortedFlags都是Map类型,每一个请求都会创建一个实例,随着时间的推移和请求的增多,如何防止缓存雪崩问题?

答:signals和abortedFlags准确的说是WeakMap类型,而WeakMap跟Map会有所区别,WeakMap的键只能是对象的引用,当垃圾回收机制执行时,会检测WeakMap的键是否被引用,若没有被引用,该键对会被删除,并自动回收,从而防止缓存雪崩的问题。

AbortSignal是如何具备监听事件能力的?

答:它本身并不具备事件处理能力,它继承了一个EventTarget类使其具备监听处理事件能力

参考文章:

一个可中断请求fetch的原理分析和应用 https://zhuanlan.zhihu.com/p/416572062

[译] 使用 AbortController 终止 fetch 请求 https://juejin.cn/post/6844904072051425293

一个可中断请求fetch的原理分析和应用(之前的笔记) https://github.com/ctq123/blogs/issues/9

AbortController使用场景探索 https://www.jianshu.com/p/2f23c33e1922

转载本站文章《中断操作:AbortController学习笔记》,
请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/html5/2022_0530_8824.html

中断操作:AbortController学习笔记的更多相关文章

  1. PHP操作MongoDB学习笔记

    <?php/*** PHP操作MongoDB学习笔记*///*************************//**   连接MongoDB数据库  **////*************** ...

  2. Flas-SQLAchemy数据库操作使用学习笔记

    Flas-SQLAchemy数据库操作使用学习笔记 Flask-SQLALchemy 是一个给你的应用添加 SQLALchemy 支持的 Flask 扩展.SQLALchemy 是Python语言的S ...

  3. delphi操作xml学习笔记 之一 入门必读

    Delphi 对XML的支持---TXMLDocument类       Delphi7 支持对XML文档的操作,可以通过TXMLDocument类来实现对XML文档的读写.可以利用TXMLDocum ...

  4. zepto源码--核心方法5(文本操作)--学习笔记

    涉及到文本内容的主要有三个函数:html, text, val. 我们已经见过多次,一个函数多种用途的情况,今天这三个函数也不例外,既可以获取内容,也可以设置内容.判断条件就是有没有传入参数,如果没有 ...

  5. 在多线程中进行UI操作--ios学习笔记

    iOS 上不建议在非主线程进行UI操作,在非主线程进行UI操作有很大几率会导致程序崩溃,或者出现预期之外的效果. 我开始不知道这一点,在子线程中进行了弹窗操作,结果程序就出问题了! 报的错误是(EXC ...

  6. Java语言中IO流的操作规律学习笔记

    1,明确源和目的. 数据源:就是需要读取,可以使用两个体系:InputStream.Reader: 数据汇:就是需要写入,可以使用两个体系:OutputStream.Writer: 总结: 读:就是把 ...

  7. c#之文件操作(学习笔记)

    File类和Directory类 FileInfo类 需要提供一个文件路径来创建一个FileInfo类实例对象,FileInfo提供很多类似File的方法,在选择使用File还是FileInfo时应遵 ...

  8. python操作execl学习笔记(一)

    本节只记录关于execl的读操作: execl 内容及格式 python3 #!/usr/bin/env python #-*- coding:utf-8 -*- import xlrd import ...

  9. BZOJ1500: [NOI2005]维修数列 [splay序列操作]【学习笔记】

    以前写过这道题了,但我把以前的内容删掉了,因为现在感觉没法看 重写! 题意: 维护一个数列,支持插入一段数,删除一段数,修改一段数,翻转一段数,查询区间和,区间最大子序列 splay序列操作裸题 需要 ...

  10. 使用curator框架简单操作zookeeper 学习笔记

    Curator 操作是zookeeper的优秀api(相对于原生api),满足大部分需求.而且是Fluent流式api风格. 参考文献:https://www.jianshu.com/p/70151f ...

随机推荐

  1. JS异步任务的并行、串行,以及二者结合

    让多个异步任务按照我们的想法执行,是开发中常见的需求.今天我们就来捋一下,如何让多个异步任务并行,串行,以及并行串行相结合. 一.并行 并行是使用最多的方式,多个相互间没有依赖关系的异步任务,并行执行 ...

  2. 04-23: dataclasses使用方法

    vehicle_seeds: List[int] = dataclasses.field(default_factory=list) dataclasses 模块提供了一种简洁的方式来定义Python ...

  3. JVM 学习

    目录 1. 类加载器及类加载过程 1.1 基本流程 1.2 类加载器子系统作用 1.3 类加载器角色 1.4 加载过程 (1) 加载 loading (2) 链接 linking 验证 verify ...

  4. 电路中的N.M.缩写含义

    国外的一些电路中会发现在一些器件旁会有 N.M. 的标注. N.M. = Not Mount

  5. 企业微信获取code

    String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+AuthUtil.APPID   + ...

  6. 基于JuiceFS 的低成本 Elasticsearch 云上备份存储

    杭州火石创造是国内专注于产业大数据的数据智能服务商,为了解决数据存储及高效服务客户需求,选择了 Elasticsearch 搜索引擎进行云上存储.基于性能和成本的考虑,在阿里云选择用本地 SSD EC ...

  7. 递归+DP:爬楼梯问题

        一只青蛙一次可以跳上 1 级台阶,也可以跳上2 级.求该青蛙跳上一个n 级的台阶总共有多少种跳法. 输入格式: 首先输入数字n,代表接下来有n组输入,50>=n>=0,然后每行一个 ...

  8. 微信小程序记住密码,让登录解放双手

    密码是用户最重要的数据,也是系统最需要保护的数据,我们在登录的时候需要用账号密码请求登录接口,如果用户勾选记住密码,那么下一次登录时,我们需要将账号密码回填到输入框,用户可以直接登录系统.我们分别对这 ...

  9. 一行代码解决IE停用后无法继续使用IE弹窗功能的问题

    微软在2023年2月14日通过Edge浏览器更新,彻底封死IE.Windows Update中没有记录.开始菜单中的IE以及桌面IE图标双击自动打开Edge,默认程序设置了IE也没有任何效果,仅能通过 ...

  10. Codeforces Round 909 (Div3)(本菜鸟只补到了E)

    Codeforces Round 909 (Div.3) A. Game with Integers 水题,就是可以被3整除的输出"Second",不能被3整除的输出"F ...