前端面试一般喜欢问:

在看来《使用 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. FreeSWITCH的moh使用笔记

    操作系统 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 之前写过FreeSWITCH安装的文章,今天整理下moh使用过程中遇到的问题及解决方案,并提供moh音频下载途径.F ...

  2. EFCore 使用FluntApi配置 全局查询筛选器

    我们在类中通常会有一个属性为 IsDel来表示软删除或也称逻辑删除,这个属性会导致我们在进行查询操作时,每一次都要 .where(s=>s.IsDel==false) 非常的麻烦.在使用efCo ...

  3. k8s-服务网格实战-配置 Mesh(灰度发布)

    在上一篇 k8s-服务网格实战-入门Istio中分享了如何安装部署 Istio,同时可以利用 Istio 实现 gRPC 的负载均衡. 今天我们更进一步,深入了解使用 Istio 的功能. 从 Ist ...

  4. git中的ole mode 和 new mode提示问题

    git status 显示如下(文件内容其实并没有改变): old mode 100644 new mode 100755 原因是: 使用chmod修改过文件权限后,filemode会有变化. 解决办 ...

  5. Python+Yolov8+ONNX实时缺陷目标检测

    相比于上一篇Windows10+Python+Yolov8+ONNX图片缺陷识别,并在原图中标记缺陷,有onnx模型则无需配置,无需训练. 优化了程序逻辑,降低了程序运行时间,增加了实时检测功能 目录 ...

  6. Proj4:改进LiteOS中物理内存分配算法

    Proj4:改进LiteOS中物理内存分配算法 实验目的 掌握LiteOS系统调用的自定义方法 实验环境 Ubantu和IMX6ULL mini 实验内容 (从代码角度详细描述实验的步骤和过程) 原先 ...

  7. HarmonyOS 开发入门(二)

    HarmonyOS 开发入门(二) 日常逼逼叨 在HarmonyOS 开发入门(一)中我们描述了 HarmonyOS 开发的语言ArKTs以及Ts简单的入门级语法操作,接下来我们进入第二部分Harmo ...

  8. 【Spring Boot】【外包杯】学习day01 | 项目目录结构划分以及代码分层

    起因:扒了一个开源的项目,但是啃起来很硬,所以决定开始学习相关的知识. 我们之前的SSM项目,搭建过程较为繁琐: 1)配置 web.xml,加载 spring 和 spring mvc 2)配置数据库 ...

  9. 一文秒懂|Linux字符设备驱动

    1.前言 众所周知,Linux内核主要包括三种驱动模型,字符设备驱动,块设备驱动以及网络设备驱动. 其中,Linux字符设备驱动,可以说是Linux驱动开发中最常见的一种驱动模型. 我们该系列文章,主 ...

  10. 07-Shell运算符

    1.算术运算符 1.1 expr命令 expr 是 evaluate expressions 的缩写,译为"求值表达式".Shell expr 是一个功能强大,并且比较复杂的命令, ...