如何优雅地中断 Promise?来试试 AbortController 吧

本文介绍了如何使用AbortController和AbortSignal终止Fetch请求,详细阐述了如何终止单个和多个请求,以及这两个对象的属性和方法。文章还探讨了在开发中的其他使用场景,包括取消事件监听和在Node.js中的应用。
摘要由CSDN通过智能技术生成

欢迎大家来到 前端小课堂 的第五期,今天我们来聊一聊如何终止正在进行中的 Fetch 以及 Promise。文中会跟大家详细介绍这里面的两个关键知识点 AbortControllerAbortSignal。对动手实践比较感兴趣的同学还可以看对应的视频版本

大家在平时的开发过程中估计不会经常碰到需要主动取消一个 Fetch 请求的需求,所以一部分同学可能对这一块知识不是很了解。没有关系,看完这篇文章你就能够掌握关于如何终止一个 Fetch 请求或者一个 Promise 的全部技能了。那我们赶快开始吧~

这篇文章比我预期要花费的时间和精力还要多,所以文章比较长,大家现在没时间浏览的可以先收藏起来,以后慢慢看。如果觉得这篇文章不错的话,也可以帮忙点个赞,转发支持一下。

使用 AbortController 终止 Fetch 请求

fetch 之前,我们请求后端的资源使用的方式是通过 XMLHttpRequest 这个构造函数,创建一个 xhr 对象,然后通过这个 xhr 对象进行请求的发送以及接收。

const xhr = new XMLHttpRequest();
xhr.addEventListener('load', function (e) {
   
  console.log(this.responseText);
});
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
xhr.send();

这个 xhr 上也存在一个 abort 方法用来进行请求的终止操作。但是需要注意的是,这个 abort 的执行过程是比较模糊的。 我们不清楚 abort 在什么时候可以不进行或终止对应的网络请求,又或者如果在调用 abort 方法和获取到请求的资源之间存在竞争条件的时候会发生什么。我们可以通过简单的代码来实践一下:

// ... 省略掉上面的代码
setTimeout(() => {
   
  xhr.abort();
}, 10);

通过添加一个延时,然后取消掉对应的请求;在控制台可以看到,有时请求已经获取到结果了,但是却没有打印出对应的结果;有时请求没有获取到对应的结果,但是查看对应的网络的状态却是成功的。所以这里面有很多的不确定性,跟我们的感觉是比较模糊的。
等到 fetch 出来的时候,大家就在讨论关于如何正确,清楚地取消一个 fetch 请求。最早的讨论可以看这里 Aborting a fetch #27 ,那已经是7年前(2015年)的事情了,可以看到当时的讨论还是比较激烈的。大家感兴趣的话可以看看当时大家都主要关注的是哪些特性。

最终,新的规范 出来了,通过 AbortControllerAbortSignal 我们可以方便,快捷,清楚地终止一个 fetch 请求。要注意的是,这个规范是一个 DOM 层面的规范,不是 JavaScript 语言层面的规范。现在绝大多数的浏览器环境和新版本的 Node.js 环境也都支持这个特性了。关于 AbortController 的兼容性,大家可以参考这里 AbortController#browser_compatibility

下面文章中的代码例子基本上都可以直接复制粘贴到控制台运行的,所以感兴趣的同学阅读到对应的部分可以直接打开浏览器的控制台去运行一下,然后看看对应的结果。加深一下自己对相关知识点的记忆。

终止正在进行中的单个请求

我们先通过一段代码来给大家展示一下如何实现这个功能

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

const resourceUrl = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(resourceUrl, {
    signal })
  .then(response => response.json())
  .then(json => console.log(json))
  .catch(err => {
   
    // 不同浏览器的返回结果不同
    console.log(err);
  });

// 可以立即终止请求,或者设置一个定时器
// ac.abort();
setTimeout(() => {
   
  ac.abort();
}, 10);

大家感兴趣的话也可以把上面的代码复制粘贴到浏览器的控制台运行一下,上面代码的运行结果如下所示:

image.png
image.png

可以看到控制台的 Console 的输出是:DOMException: The user aborted a request.
对应的 Network 展示的是一个取消状态的请求。这说明我们刚才发送的请求被终止取消掉了。
能够在一些特定的情况下主动地取消相关的请求对我们应用来说是很重要的,这能够减少我们用户的流量使用以及我们应用的内存使用。

AbortController 的深入剖析

接下来我们来讲解一下上面的代码,第一行通过 AbortController 创建了一个 AbortController 类型的实例 ac,这个实例上有一个 abort 方法和一个 AbortSignal 类型的 signal 实例。然后我们通过 fetch 方法去请求一个资源路径,传递给 fetch 的选项把 acsignal 对象传递进去。fetch 方法如果获取到了资源就会把资源打印到控制台,如果网络发生了问题,就会捕获异常,然后把异常打印到控制台。最后,通过一个 setTimeout 延时,调用 acabort 方法终止 fetch 请求 。

fetchoptions 选项允许我们传递一个 signal 对象;fetch 的内部会监测这个对象的状态,如果这个对象的状态从未终止的状态变为终止的状态的话,并且 fetch 请求还在进行中的话,fetch 请求就会立即失败。其对应的 Promise 的状态就会变为 Rejected

如何改变 signal 的状态呢?我们可以通过调用 acabort 方法去改变 signal 的状态。一旦我们调用了 ac.abort() 那么与之关联的 signal 的状态会立刻从起始状态(非终止状态)转变为终止状态。

我们上面只是简单地使用了 signal 对象,这个对象是 AbortSignal 类的实例,对于 AbortSignal 我们下面会做深入的讲解,这里暂时只需要知道 signal 可以作为一个信号对象传递给 fetch 方法,可以用来终止 fetch 的继续进行。
另外,在不同的浏览器中打印的结果可能略有不同,这个跟不同浏览器的内部实现有关系。比如在 Firefox 中的结果如下:

image.png
image.png

Safari 中的结果如下:

image.png
image.png

当然如果我们没有终止 fetch 请求的话,控制台的打印将会是:

image.png

另外大家如果需要一些模拟的数据接口的话可以试试 JSONPlaceholder ,还是很方便使用的。

批量取消多个 fetch 请求

值得注意的

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值