Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >中断操作:AbortController学习笔记

中断操作:AbortController学习笔记

原创
作者头像
周陆军博客
发布于 2022-06-25 08:50:04
发布于 2022-06-25 08:50:04
95100
代码可运行
举报
文章被收录于专栏:前端博客前端博客
运行总次数:0
代码可运行

前端面试一般喜欢问:

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

AbortController背景介绍

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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源码解析

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
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源码解析

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
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来控制。

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  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怎么写呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
AbortController的使用
提前终止后这个请求在 network 面板中的 status 显示为 canceled
ACK
2022/06/27
1.2K0
AbortController的使用
面试官:如何中断一个网络请求?
金三银四,又快到了新的一年换工作的时候了,各种妖魔鬼怪的问题又出来了,比如 "如何根据手机壳颜色来动态改变手机主题颜色",虽然这个不是面试题,但是现在想想还是依然很好笑。
用户9078190
2022/10/28
9870
面试官:如何中断一个网络请求?
如何取消 JavaScript 中的异步任务
有时候执行异步任务可能是很困难的,尤其是在特定的编程语言不允许取消被错误启动或不再需要的操作时。幸运的是 JavaScript 提供了非常方便的功能来中止异步活动。在本文中,你可以学到如何创建可中止的函数。
疯狂的技术宅
2020/03/26
3.5K0
如何取消 JavaScript 中的异步任务
AbortSignal:以前我没得选,现在我想中止promise
AbortSignal的出现使promise从语义上变为可中止的。并且,只要符合规范,所有异步操作都能变为「可中止的」。
公众号@魔术师卡颂
2021/10/12
9630
Node.js v15.x 新特性 — 控制器对象 AbortController
Node.js v15.0.0 提供了一个全局实用 API AbortController,用于在选定的基于 Promise API 中发出取消信号。无需引入在所有模块中均可使用,该 API 的实现是基于浏览器中的 Web API AbortController。
五月君
2021/04/02
1.4K0
JavaScript 函数回调风险
上述的示例运行良好,直到 some-library 更新。但并不是 some-library 库导致的 – 因为其从未将 toReadableNumber 设计为 array.map 的回调。
奋飛
2021/08/31
4990
那些关于DOM的常见Hook封装(一)
本文是深入浅出 ahooks 源码系列文章的第十四篇,这个系列的目标主要有以下几点:
GopalFeng
2022/08/01
7410
那些关于DOM的常见Hook封装(一)
那些你熟悉而又陌生的函数
我们最常用的是语法中的第二种 var timeoutID = scope.setTimeout(function[, delay]); 举个例子
苏南
2021/07/29
7360
JavaScript高级技巧
以上代码要返回true,value必须是一个数组,而且还必须与Array构造函数在同个全局作用域中。(Array是window的属性)如果value是在另外一个iframe中定义的数组,上述代码则返回false。 注意:BOM的核心对象时window,它表示浏览器的一个实例。在浏览器中,window对象有双重角色,它既是通过JavaScript访问浏览器窗口的一个接口,又是ECMAScript规定的global对象。 解决上述问题: Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串。
奋飛
2019/08/15
1.1K0
全面分析前端的网络请求方式
这种交互的的缺陷是显而易见的,任何和服务器的交互都需要刷新页面,用户体验非常差, Ajax的出现解决了这个问题。 Ajax全称 AsynchronousJavaScript+XML(异步 JavaScript和 XML)
ConardLi
2019/05/23
1.9K0
解决前端常见问题:竞态条件
| 导语 竞态条件一词翻译自英语 "race conditions"。当我们在开发前端 web 时,最常见的逻辑就是从后台服务器获取并处理数据然后渲染到浏览器页面上,过程中有不少的细节需要注意,其中一个就是数据竞态条件问题,本文会基于 React 并结合一个小 demo 来解释何为竞态条件,以及循序渐进地介绍解决竞态条件方法。框架不同解决的方式会不一样,但不影响理解竞态条件。 获取数据 下面是一个小 demo:前端获取文章数据,并渲染到页面上 App.tsx import React from 'react
用户1097444
2022/06/29
1.3K0
解决前端常见问题:竞态条件
太冤了!群友因为这个功能的实现没回答好,到手的 offer 飞了。来看看 React19 如何解决
之前一个群友「三年经验」找我诉苦。说遇到一个奇葩的面试,千辛万苦过了三面,等对方团队着发 offer 的时候,结果对方 hr 说有另外一个人也过了面试,各方面都跟他差不多。所以对方团队不知道如何抉择,想要加面一个面试题,聊十分钟来最终决定选谁。
用户6901603
2024/05/09
1020
太冤了!群友因为这个功能的实现没回答好,到手的 offer 飞了。来看看 React19 如何解决
Fetch API 教程
fetch()是 XMLHttpRequest 的升级版,用于在 JavaScript 脚本里面发出 HTTP 请求。
ruanyf
2020/12/30
3K0
Fetch API 教程
JavaScript 中如何取消请求
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情
掘金安东尼
2022/09/19
1.3K0
JavaScript 中如何取消请求
Axios源码笔记 | 深入剖析,Helpers工具库全景深度解析
Axios 是一个基于 Promise 的 HTTP 客户端,广泛应用于前端开发中,用于发送 HTTP 请求。其源码结构清晰,其中 Helpers 工具库包含了许多实用的工具函数,这些函数为 Axios 的核心功能提供了有力的支持。
叶一一
2025/04/19
1200
Axios源码笔记 | 深入剖析,Helpers工具库全景深度解析
Axios 源码笔记 | 深入剖析 Adapters 适配器源码,揭开请求处理的神秘面纱
在前端开发中,Axios 是一个广泛使用的基于 Promise 的 HTTP 客户端,它可以在浏览器和 Node.js 环境中工作。Axios 的强大之处在于其模块化的设计,其中适配器(Adapters)模块起着关键作用,它允许 Axios 在不同的环境中使用不同的请求方式。本文将深入解析 axios-1.x/lib/adapters 目录下的主要文件,包括 adapters.js、fetch.js、http.js 和 xhr.js,带你了解其实现原理和设计思路。
叶一一
2025/04/20
1160
Axios 源码笔记 | 深入剖析 Adapters 适配器源码,揭开请求处理的神秘面纱
Axios 源码笔记 | Cancel 请求取消体系解构,从设计哲学到生产实践的全链路剖析
在现代前端工程实践中,网络请求管理已从简单的收发数据演变为复杂的流程控制。当用户快速切换页面、重复提交表单或执行高频率操作时,未完成的冗余请求可能引发以下问题:
叶一一
2025/04/22
1370
Axios 源码笔记 | Cancel 请求取消体系解构,从设计哲学到生产实践的全链路剖析
JS 中的网络请求 AJAX, Fetch, WebSocket
AJAX 是 Asynchronous JavaScript And XML 的简称,它可以让页面在不刷新的情况下从服务器获取数据。
羽月
2022/10/08
4.3K0
jimojianghu
有天,辛苦做了个复杂操作功能的页面,上线后有用户反馈:很多功能使用不了。惊了,以为是哪里出了bug。 立马联系用户,才发现原来是用户使用笔记本电脑,没有禁用触控板,然后不小心碰到导致整个界面都放大,很多功能超出界面不见了。 然而那能怎么办,用户第一,自然得快速解决这个问题,而且是触控板和触摸屏都得解决。
落寞的鱼丶
2022/02/07
3.9K0
如何解决前端常见的竞态问题
会先后请求 data2 与 data3,分页器显示当前在第三页,并且进入 loading。
Leecason
2022/12/16
2K0
如何解决前端常见的竞态问题
相关推荐
AbortController的使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验