如何优雅地取消 JavaScript 异步任务?

在程序中处理异步任务通常比较麻烦,尤其是那些不支持取消异步任务的编程语言。所幸的是,JavaScript 提供了一种非常方便的机制来取消异步任务。

中断信号 AbortSignal

自从 ES2015 引入了 ,开发者有了取消异步任务的需求,随后推出的一些 Web API 也开始支持异步方案,比如 Fetch API。TC39 委员会(就是制定 ECMAScript 标准的组织)最初尝试定义一套通用的解决方案,以便后续作为 ECMAScript 标准。但是后来讨论不出什么结果来,这个问题也就搁置了。

鉴于此,WHATWG (HTML 标准制定组织)另起炉灶,自己搞出一套解决方案,直接在 DOM 标准上引入了 。这种做法的坏处显而易见,因为它不是语言层面的 ECMAScript 标准,因此 Node.js 平台也就不支持 。

在 DOM 规范里, 设计得非常通用,因此事实上你可以用在任何异步 API 中。目前只得到 Fetch API 的官方支持,但你完全可以用在自己的异步代码里。

在开始介绍之前,我们先看下 的工作原理:

上面的代码很简单,首先创建了的一个实例(1),并将它的 属性赋值给一个变量(2)。然后调用并传入 参数(3)。取消请求时调用 (4)。这样就会自动执行 的 reject ,也就是进入部分(5)。

它的属性是核心所在。该属性是 DOM 接口的实例,它有一个 属性,带有是否调用了 的相关信息。还可以在上面监听事件,该事件在调用时触发。简单来说, 就是的一个公开接口。

可取消的函数

假设有一个执行复杂计算的异步函数,为简单起见,我们就用定时器模拟:

可能的情况是,用户想取消这种耗时的任务。我们用一个按钮来开始和停止:

上面的代码给按钮绑定了一个异步的 事件处理器(1),并在里面调用了 函数(2)。5 秒后会弹出对话框显示结果(3)。顺便提一下,可以让 JavaScript 代码进入严格模式,跟的效果一样。

增加中断异步任务的功能:

代码变长了很多,但是别慌,理解起来也不是很难。

最外层的代码块(1)相当于一个 IIFE(立即执行的函数表达式),这样变量 (2)就不会污染全局了。

首先把它的值设为,并且它的值随着按钮点击而改变。随后给它赋值为的一个实例(3),再把实例的属性直接传给 函数(4)。

如果用户在 5 秒之内再次点击按钮,就会执行函数(5)。这样就会在刚才传给 的实例上触发 事件(6)。

在 事件处理器里面清除定时器(7),然后用一个适当的异常对象拒绝 Promise(8)。

根据 DOM 规范,这个异常对象必须是一个类型的 。

这个异常对象最终传给了 (9) 和 (10)。

但是还要考虑这样一种情况:

这种情况下 事件不会触发,因为它在传给 函数前就执行了。为此我们需要改造下代码:

异常对象的定义移到了顶部(1),这样就可以在两个地方重用了。另外,多了个条件判断(2)。如果它的值是,函数应该立即拒绝 Promise,没必要再往下执行了。

到这里我们就实现了一个完整的可取消的异步函数,以后碰到需要处理异步任务的地方就可以派上用场了。

其实仔细一想,这不就是一个发布-订阅模式的应用吗?如果平台不支持AbortController,完全可以自己实现一个。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200320A0L9KD00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券