如何优雅地中断 Promise?

大家在平时的开发过程中估计不会经常碰到需要主动取消一个 Fetch 请求的需求,所以一部分同学可能对这一块知识不是很了解 。没有关系,看完这篇文章你就能够掌握关于如何终止一个 Fetch 请求或者一个 Promise 的全部技能了 。那我们赶快开始吧~
这篇文章比较长,大家现在没时间浏览的可以先收藏起来,以后慢慢看 。如果觉得这篇文章不错的话,也可以帮忙点个赞 。
使用 AbortController 终止 Fetch 请求
在 之前,我们请求后端的资源使用的方式是通过 这个构造函数,创建一个 对象,然后通过这个 对象进行请求的发送以及接收 。
这个 上也存在一个 方法用来进行请求的终止操作 。但是需要注意的是,这个 的执行过程是比较模糊的 。我们不清楚 在什么时候可以不进行或终止对应的网络请求,又或者如果在调用 方法和获取到请求的资源之间存在竞争条件的时候会发生什么 。我们可以通过简单的代码来实践一下:
通过添加一个延时,然后取消掉对应的请求;在控制台可以看到,有时请求已经获取到结果了,但是却没有打印出对应的结果;有时请求没有获取到对应的结果,但是查看对应的网络的状态却是成功的 。所以这里面有很多的不确定性,跟我们的感觉是比较模糊的 。
等到 出来的时候,大家就在讨论关于如何正确,清楚地取消一个 请求 。最早的讨论可以看这里 Aborting a fetch #27 ,那已经是7年前(2015年)的事情了,可以看到当时的讨论还是比较激烈的 。大家感兴趣的话可以看看当时大家都主要关注的是哪些特性 。
最终,新的规范 出来了,通过 和 我们可以方便,快捷,清楚地终止一个 请求 。要注意的是,这个规范是一个 DOM 层面的规范,不是 JAVAScript 语言层面的规范 。现在绝大多数的浏览器环境和新版本的 Node.js 环境也都支持这个特性了 。关于 的兼容性,大家可以参考这里 AbortController#browser_compatibility
下面文章中的代码例子基本上都可以直接复制粘贴到控制台运行的,所以感兴趣的同学阅读到对应的部分可以直接打开浏览器的控制台去运行一下,然后看看对应的结果 。加深一下自己对相关知识点的记忆 。
终止正在进行中的单个请求
我们先通过一段代码来给大家展示一下如何实现这个功能
大家感兴趣的话也可以把上面的代码复制粘贴到浏览器的控制台运行一下,上面代码的运行结果如下所示:
可以看到控制台的 Console 的输出是:DOMException: The user aborted a request.对应的 这说明我们刚才发送的请求被终止取消掉了 。能够在一些特定的情况下主动地取消相关的请求对我们应用来说是很重要的,这能够减少我们用户的流量使用以及我们应用的内存使用 。
AbortController 的深入剖析
接下来我们来讲解一下上面的代码,第一行通过 创建了一个 类型的实例 ,这个实例上有一个 方法和一个 类型的 实例 。然后我们通过 方法去请求一个资源路径,传递给 的选项把 的 对象传递进去 。
方法如果获取到了资源就会把资源打印到控制台,如果网络发生了问题,就会捕获异常,然后把异常打印到控制台 。最后,通过一个 延时,调用 的 方法终止 请求。
的 选项允许我们传递一个 对象; 的内部会监测这个对象的状态,如果这个对象的状态从未终止的状态变为终止的状态的话,并且 请求还在进行中的话,请求就会立即失败 。其对应的 的状态就会变为。
如何改变 的状态呢?我们可以通过调用 的 方法去改变 的状态 。一旦我们调用了 那么与之关联的 的状态会立刻从起始状态(非终止状态)转变为终止状态 。
我们上面只是简单地使用了 对象,这个对象是 类的实例,对于 我们下面会做深入的讲解,这里暂时只需要知道 可以作为一个信号对象传递给 方法,可以用来终止 的继续进行 。另外,在不同的浏览器中打印的结果可能略有不同,这个跟不同浏览器的内部实现有关系 。比如在 Firefox 中的结果如下:
在 Safari 中的结果如下:

如何优雅地中断 Promise?

文章插图
当然如果我们没有终止 请求的话,控制台的打印将会是:
另外大家如果需要一些模拟的数据接口的话可以试试 JSONPlaceholder ,还是很方便使用的 。
批量取消多个 fetch 请求
值得注意的是,我们的 对象可以同时传递给多个请求,在需要的情况下可以同时取消多个请求;我们来看看如何进行这样的操作 。代码如下所示:


推荐阅读