IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> AbortController的使用 -> 正文阅读

[网络协议]AbortController的使用

今天介绍一个有用的 JavaScript api AbortController

AbortController是什么

AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。你可以使用 AbortController.AbortController() 构造函数创建一个新的 AbortController。使用 AbortSignal 对象可以完成与 DOM 请求的通信

这个 api 简单来说就是可以提供一个能力给我们去提前终止一个 fetch 请求

一个终止 fetch 请求的 demo 如下:

fetchButton.onclick = async () => {
  const controller = new AbortController();

  // 点击abort button实现终止fetch请求
  abortButton.onclick = () => controller.abort();

  try {
    const r = await fetch('/json', { signal: controller.signal });
    const json = await r.json();
  } catch (e) {
  // 如果fetch请求被终止会抛出一个AbortError的错误
    const isUserAbort = (e.name === 'AbortError');
  }
};

提前终止后这个请求在 network 面板中的 status 显示为 canceled

在没有AbortController这个 api 之前,我们是没法去让浏览器提前去终止一个请求的。而有了这个 api 之后,浏览器就能提前终止请求进而节约一些用户带宽。除此之外,这个 api 也能给我们带来一些新的开发模式

Controller 和 Signal

下面实例化了一个AbortController,它的signal属性就是一个AbortSignal

const controller = new AbortController();
const { signal } = controller;
  • controller 可通过controller.abort()去终止它对应的signal
  • signal本身是不能被直接终止的。可以将它传递给一些函数调用如 fetch 或者直接监听signal的状态变化(可以通过signal.aborted查看signal的状态或者监听它的abort事件)

实际使用

普通对象中的终止

一些旧的 DOM api 是不支持AbortSignal。例如WebScocket只提供了一个close方法当我们无需使用时进行关闭。如果要使用AbortSignal则可以类似以下的封装

function abortableSocket(url, signal) {
  const w = new WebSocket(url);

  if (signal.aborted) {
    w.close();  // signal已经终中止的情况下马上关闭websocket
  }
  signal.addEventListener('abort', () => w.close());

  return w;
}

这个使用也很简单,但是需要注意的是如果signal已经终止的情况下是不会触发abort事件,需要我们先进行一个判断是否signal已经终止

移除事件监听

我们经常需要在 js 中处理 dom 的监听和卸载工作。但是下面的例子由于事件监听和卸载传入的函数不是同一个引用时不会生效的

window.addEventListener('resize', () => doSomething());

// 不会生效
window.removeEventListener('resize', () => doSomething());

因此我们经常需要一些额外的代码去维护这个回调函数的引用的一致性。而有了AbortSignal之后我们就可以有一种的新的方式去实现

const controller = new AbortController();
const { signal } = controller;

window.addEventListener('resize', () => doSomething(), { signal });

controller.abort();

因为addEventListener也能接收signal属性的。我们最后只需要调用controller.abort(),这个controllersignal传递的相关事件监听都会被自动相应卸载了

构造器模式

在 JavaScript 中我们可能需要在对象中管理非常复杂的生命周期,如WebSocket。我们需要执行开启然后执行一系列逻辑后终止。可能我们会写以下代码

const someObject = new SomeObject();
someObject.start();

// 执行一些操作后
someObject.stop();

也可以通过AbortSignal进行实现

const controller = new AbortController();
const { signal } = controller;

const someObject = new someObject(signal);

// 执行一些操作后
controller.abort();
  1. 这能非常清晰地表示这个对象只能被执行一次,只能从开始到结束,而不能反过来。如果它终止了后想再次使用则需要再次创建一个对象

  2. 可以在很多地方共享一个signal。我们无需持有多个SomeObject的实例。只需要调用controller.abort(),这些SomeObject的实例都能被终止掉

  3. 如果SomeObject内部也有调用像fetch之类的内部 api 只需要把这个signal继续传递,则fetch也能被一起终止掉

如下是一个例子。展示了两种 signal 的用法。传递给内置 apifetch和检查signal状态执行一些操作

export class SomeObject {
  constructor(signal) {
    this.signal = signal;

    // 执行一些操作例如发请求
    const p = fetch('/json', { signal });
  }

  doComplexOperation() {
    if (this.signal.aborted) {
      throw new Error(`thing stopped`);
    }
    for (let i = 0; i < 1_000_000; ++i) {
      // 执行复杂操作
    }
  }
}
react hook 中的异步调用

我们通常会在useEffect中进行一些异步 api 调用。借助signal可以在下一次useEffect重新调用 api 的时候将前一次的调用终止

function FooComponent({ something }) {
  useEffect(() => {
    const controller = new AbortController();
    const { signal } = controller;

    const p = (async () => {
      const j = await fetch(url + something, { signal });
    })();

    return () => controller.abort();
  }, [something]);

  return <>...<>;
}

也可以封装一个useEffectAsync的 hook

function useEffectAsync(cb,dependence) {
   const controller = new AbortController();
   const { signal } = controller;
   useEffect(() => {
     cb(signal);
     return () => controller.abort();
   },dependence)
}

一些有用的 AbortSignal 方法

这些方法当前有可能还没有实现

function abortTimeout(ms) {
  const controller = new AbortController();
  setTimeout(() => controller.abort(), ms);
  return controller.signal;
}
  • AbortSignal.any(signals):创建一个AbortSignal,如果传入的任一signal终止了,这个返回的signal也会被终止
function abortAny(signals) {
  const controller = new AbortController();
  signals.forEach((signal) => {
    if (signal.aborted) {
      controller.abort();
    } else {
      signal.addEventListener('abort', () => controller.abort());
    }
  });
  return controller.signal;
}
  • AbortSignal.throwIfAborted():如果signal本身已经终止了,调用该方法会抛出执行abort(reason)时指定的 reason 异常;否则只会静默执行
  if (signal.aborted) {
    throw new Error(...);
  }
  // becomes
  signal.throwIfAborted();

这个方法目前不太容易 polyfill,但是可通过下面的工具函数实现

function throwIfSignalAborted(signal) {
  if (signal.aborted) {
    throw new Error(...);
  }
}

参考

https://whistlr.info/2022/abortcontroller-is-your-friend/

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-06-29 19:26:03  更:2022-06-29 19:26:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:23:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码