如何在 Promise 执行过程中取消未完成的任务?

Promise 是一种用于异步编程的 JavaScript 库。它提供了一种简单而强大的方法来处理异步操作,以及确保异步操作的可组合性和错误处理能力。然而,有时候我们需要取消 Promise 中未完成的任务,这时候该怎么办呢?本文将从 Promise 基本用法的角度出发,探讨如何在 Promise 执行过程中取消未完成的任务。

Promise 基本用法回顾

在正式讲解 Promise 取消机制之前,我们有必要先回顾一下 Promise 基本用法。

Promise 状态

Promise 有三个状态:Pending (等待态)、Fulfilled (完成态)和 Rejected (失败态)。

当 Promise 状态为 Pending 时,表示此时异步操作还未完成。

当 Promise 状态为 Fulfilled 时,表示异步操作已经完成,并且成功地返回了结果。

当 Promise 状态为 Rejected 时,表示异步操作已经完成,但是发生了错误或者异常。

Promise 构造函数

我们可以使用 Promise 构造函数来创建一个 Promise 实例。

----- ------- - --- ----------------- ------- -- -
  -- ----
  -- ----- ------- -----------
  -- ----- ------ ---------
---

在 Promise 构造函数中,我们要传入一个参数,这个参数是一个函数,它接受两个参数,resolve 和 reject。resolve 和 reject 函数分别用来将 Promise 的状态设置为 Fulfilled 和 Rejected,并且传递异步操作的结果或者错误信息。当异步操作完成后,我们需要调用 resolve 或者 reject 函数将 Promise 实例的状态改变。

Promise 链式调用

Promise 链式调用是 Promise 的一个重要特性。我们可以在一个 Promise 实例的 resolved 回调函数里再次返回一个 Promise 实例,从而实现多个异步操作的组合。这样,我们就可以避免回调地狱的问题。

----- ------- - ------------
  ------------- -- -
    ------ --------------------
  --
  ------------- -- -
    ------ --------------------
  --
  ------------- -- -
    ---------------------
  --
  ------------ -- -
    -------------------
  ---

在上面的例子中,我们先调用了 asyncFunc1,当它返回的 Promise 状态为 Fulfilled 时,会自动调用 then 方法中的回调函数。在回调函数中,我们返回了 asyncFunc2(result1),这样 asyncFunc2 会在 asyncFunc1 的结果基础上进行计算,并将计算结果返回给 asyncFunc3。同样地,asyncFunc3 会在 asyncFunc2 的结果基础上进行计算,并将计算结果返回给最后一个 then 方法。如果任何一个异步操作出现错误,catch 方法会被调用。

Promise.all 和 Promise.race

Promise.all 可以用来当我们需要多个异步操作都完成后再执行某个操作的时候。Promise.all 接受一个 Promise 实例组成的数组,它返回的 Promise 状态为 Fulfilled 并传递所有 Promise 实例返回结果的数组,只有当所有 Promise 实例都 fulfilled 时,Promise.all 才 fulfilled。

---------------------- --------- ----------
  ------------- -- -
    ---------------------
  --
  ------------ -- -
    -------------------
  ---

Promise.race 可以用来当我们需要一系列异步操作中最先完成的一个操作的结果时。Promise.race 接受一个 Promise 实例组成的数组,它返回的 Promise 状态为 Fulfilled 或者 Rejected,并且传递最先完成的 Promise 实例的返回结果或者错误信息。

----------------------- --------- ----------
  ------------ -- -
    --------------------
  --
  ------------ -- -
    -------------------
  ---

Promise 取消机制

Promise 取消机制是指当 Promise 实例的异步操作还未完成时,我们可以取消这个异步操作并且防止它继续执行。有时候我们需要取消 Promise 中未完成的任务,例如当用户通过输入框实时搜索数据时,如果用户快速地输入了多次搜索内容,那么前面的搜索操作就可能会被后面的搜索操作所覆盖,从而导致前面的异步操作变成无用功。

关于 Promise 取消机制的讨论

在讨论如何实现 Promise 取消机制之前,有一个问题值得我们思考:为什么 Promise 没有内置的取消机制?

其中一个原因是 Promise 本身的逻辑:Promise 实例一旦创建,它的状态就不能再改变。而这个状态的改变是由 Promise 的异步操作所决定的。也就是说,Promise 取消机制必须通过 Promise 外层的变量、函数等方式去控制 Promise 内的异步操作。而 Promise 内部总不能随便通过外部变量去控制它的异步操作。

另一个原因是 Promise 的可组合性。Promise 的链式调用可以非常方便地将多个异步操作组合起来,如果我们直接对其中某个 Promise 进行取消操作,那么这个 Promise 可能处于链式调用中的任何一个位置,它前面和后面的 Promise 的状态都不确定,这就很难保证整个异步操作的正确性。

因此,如果需要实现 Promise 取消机制,我们需要保证取消操作不会影响异步操作本身的正确性,同时还需保证取取消操作符的实用性,否则取消操作就很难被接受。

手动实现 Promise 取消机制

在现有的 JavaScript 中,并没有内置的 Promise 取消机制。不过,我们可以手动实现一些方法来实现类似的功能。本文提供两种实现方法:基于 cancalToken 的方法和基于 RxJS 的方法。

基于 cancelToken 的方法

为了实现基于 cancelToken 的方法,我们需要创建一个 CancelToken 类。这个类包含一个 cancel 属性,它是一个记录取消状态的布尔值,并且包含一个 cancel 方法,用于将 cancel 属性设置为 true。另外,我们还需要在 Promise 实例中增加一个可选参数 cancelToken,用于传入我们创建的 CancelToken 实例。在异步操作中,我们需要判断当前 cancelToken 的状态,并且进行相应的处理。

----- ----------- -
  ------------- -
    ----------- - ------
  -
  -------- -
    ----------- - -----
  -
-

-------- ----------------------- ------------ -
  --- ------------- - -----
  ----- ------------- - --- ----------------- ------- -- -
    ------------- - -- -- -
      ---------- ---------------- ------------
    --
  ---
  ----- -------------- - ---------------------- ----------------
  --------------------- - --------------
  -- ------------- -
    ------------------ - -- -- -
      ----------------
    --
  -
  ------ ---------------
-

在上面的代码中,我们首先定义了一个 CancelToken 类,这个类有一个 cancel 属性,它表示当前取消操作的状态。我们还定义了一个 makeCancelable 函数,用于将 Promise 实例转化成可取消 Promise。

makeCancelable 函数接受两个参数:promise 和 cancelToken。其中 promise 是需要转化的 Promise 实例,cancelToken 是自定义的 CancelToken 实例。我们首先创建了一个 cancelPromise,它返回的是一个 Rejected 的 Promise 实例,这个 Promise 实例的值是一个 Error 对象,用于表示当前操作被取消了。然后我们使用 Promise.race 将 cancelPromise 和 promise 组合起来,当任意一个 Promise 状态发生变化时,Promise.race 就会返回它的状态和值。

接着,我们对 promiseExector 对象增加了一个 cancel 属性,这个属性是一个函数,用于取消异步操作,并且会将 cancelPromise 的状态设置为 Rejected。

最后,我们判断传入的 cancelToken 是否存在,如果存在,则将 cancelToken 的 cancel 方法绑定到 cancelExector 并且返回 promiseExector 对象,否则直接返回 promiseExector 对象。

现在,我们可以使用 makeCancelable 函数将 Promise 实例转化为可取消的 Promise 实例,然后在异步操作中添加取消操作的代码。例如,以下代码演示了如何使用鼠标事件进行取消。

----- ----------- - --- --------------
----- ------- - ----------------------------------
--------------------------------- ----- ----- -- -
  --- -
    ----- ---- - ----- ---------------------------------- -------------
    -- ----- --- ----- -
      -- -- ---------
    -
  - ----- ------- -
    -------------------
  -
---
--------------------------------- ----- -- -
  ---------------------
---

在上面的代码中,我们首先创建了一个 CancelToken 实例 cancelToken,然后将它作为参数传递给 makeCancelable。当 input 事件触发时,我们将异步操作的结果赋值给 data。如果我们点击了 element 元素,那么事件回调函数就会调用 cancelToken 的 cancel 方法,从而取消异步操作。

基于 RxJS 的方法

除了基于 CancelToken 的方法,我们还可以使用 RxJS 库来实现 Promise 取消机制。RxJS 是 Reactive Extensions 的 JavaScript 版本,它提供了一些实用的工具函数和操作符,用于处理异步流和事件流。

在 RxJS 中,我们可以使用 Subject 类来创建一个可被取消的异步处理流,它允许我们在任何时候取消正在进行的异步操作。

首先,我们需要创建一个 Subject 实例,它用于发出 cancel 事件。在异步操作中,我们需要监听这个 Subject 实例,当它发出 cancel 事件时,我们需要进行相应的处理。

------ - -------- ---- - ---- -------
------ - --------- - ---- -----------------

----- ------- - --- ----------
------------------------
  -------------------------
  -----------
    -------- -- -
      -- ------ --------
    --
    ----- -- -
      -------------------
    --
    -- -- -
      ------------------------
    -
  --

----------------------------------------------------------- -- -- -
  ---------------
---

在上面的代码中,我们首先创建了一个 Subject 实例 cancel$,它用于发出 cancel 事件,表示我们需要取消异步操作。然后我们使用 RxJS 的 from 方法将 fetch 方法返回的 Promise 实例转化成一个 Observable 实例。最后,我们使用 takeUntil 操作符,它接受一个 Observable 实例作为参数,并返回一个新的 Observable 实例,它会在源 Observable 实例调用 complete 或者 cancel$ 实例发出 next 事件时停止发出事件。通过使用 takeUntil 操作符,我们就可以在 cancel$ 实例发出 cancel 事件时停止异步操作。

最后,我们使用事件监听器来在点击时向 cancel$ 实例发送 cancel 事件。

总结

本文详细介绍了 Promise 基本用法和 Promise 取消机制。我们首先回顾了 Promise 的基本概念和使用方法,然后讨论了 Promise 的问题以及取消机制的实现方法。如果您需要实现 Promise 取消机制,可以使用基于 CancelToken 或者 RxJS 的方法。基于 CancelToken 的方法需要定义一个 CancelToken 类和一个自定义的 makeCancelable 函数,可实现任何异步操作的取消。基于 RxJS 的方法可以使用 takeUntil 操作符和 Subject 类来实现异步操作的取消。

来源:JavaScript中文网 ,转载请联系管理员! 本文地址:https://www.javascriptcn.com/post/65310fcb7d4982a6eb2aa7c7


猜你喜欢

  • Hapi框架与Nodemailer库实现带附件的邮件发送

    介绍 在现代web应用程序中,电子邮件仍然是主要的通信方式。当涉及到邮件的发送过程时,大多数Web开发人员默认使用SMTP服务器,但它有许多限制,比如不支持HTML格式的邮件和附加文件等。

    1 年前
  • MongoDB 实现读写分离的问题解决方案!

    背景 MongoDB 是一种广泛应用的 NoSQL 数据库,它支持高扩展性、高性能、灵活的数据模型以及丰富的查询功能。在应对高并发访问时,读写分离是常见的数据库架构优化方案。

    1 年前
  • JavaScript 字符串格式化输出的解决方案:使用 ECMAScript 2017 的 String.prototype.padEnd() 方法实现对齐输出

    在前端开发中,我们经常需要通过字符串格式化输出来调整显示效果,例如在控制台输出调试信息、在页面表格中对齐显示等。然而,在 JavaScript 中并没有内置的字符串格式化输出方法,这就需要我们手动实现...

    1 年前
  • PWA 优化技巧总结

    什么是 PWA? PWA(Progressive Web App)是一种新的 Web 应用程序技术,其目标是提供类似原生应用程序的体验。PWA 可以在离线情况下工作、具有快速的加载速度和可靠的性能。

    1 年前
  • Cypress 自动化测试及其在自动化工程中的应用及优化建议

    Cypress 是一种现代化的前端自动化测试工具,它可以让开发人员快速、简便地构建和执行端到端的集成测试。Cypress 提供了一些重要功能,例如自动化测试脚本编写、自动化页面交互效果测试、自动化 A...

    1 年前
  • 在 SASS 中使用变量时出现 “Invalid CSS” 该怎么办?

    在 SASS 中使用变量时出现 “Invalid CSS” 该怎么办? SASS 是一种 CSS 预处理器,它使用类似于变量、选择器嵌套等功能来简化样式表的编写,并让开发者可以方便的重用和维护这些代码...

    1 年前
  • 使用 Koa 和 OAuth2.0 实现登录的最佳实践

    在 Web 开发中,用户登录系统是必不可少的功能。为了实现安全、灵活、扩展性等方面的考虑,使用 OAuth2.0 实现登录是一种最佳实践。本文将讲解如何使用 Koa 和 OAuth2.0 实现登录,并...

    1 年前
  • Redis 中 LIST 的插入问题及解决方案

    在 Redis 中,LIST 是一种常用的数据结构,它允许你在列表的头部或尾部快速地添加或移除元素。但是,在实际应用中,我们可能会遇到一些 LIST 的插入问题,导致数据的不一致或性能下降。

    1 年前
  • Vue.js 常用过滤器介绍及使用技巧

    Vue.js 是一款流行的前端开发框架,可以极大地提高前端开发效率和代码质量。Vue.js 有很多的扩展,过滤器是其中之一。过滤器可以让我们在模板中使用一些简单的函数来处理数据,非常方便。

    1 年前
  • 使用 Flask Restful 实现 RESTful API 的异常处理

    使用 Flask Restful 实现 RESTful API 的异常处理 RESTful API 的异常处理是开发中必不可少的一部分。良好的异常处理能够提高 API 的稳定性和可靠性,使得 API ...

    1 年前
  • 优雅地处理 CSS Reset 带来的样式问题

    CSS Reset 是指一种“重置”浏览器自带的 CSS 样式表的方法,主要的目的是消除浏览器之间的差异,使得所有浏览器渲染出的页面基本一致。然而,CSS Reset 也会带来一些样式上的问题,如字体...

    1 年前
  • 如何在 Custom Elements 中使用 Event Bus:快速传递信息

    前言 Web Components 是一种灵活、可重用的前端组件化开发方式,其中 Custom Elements 可以定义自定义元素,但是如何让这些元素之间通信呢?本文将介绍如何使用 Event Bu...

    1 年前
  • 如何解决使用 Babel 编译 ES6 时出现的 TypeError: Cannot read property 'argument' of undefined 问题

    在前端开发中,使用 ES6 编写代码已经是非常普遍的事情了,但是在编译 ES6 代码时,你可能会遇到一个很常见的错误: TypeError: Cannot read property 'argumen...

    1 年前
  • Chai 中如何使用 "own" 和 "include" 断言方法验证嵌套对象的数据

    Chai 中如何使用 "own" 和 "include" 断言方法验证嵌套对象的数据 在前端开发中,我们经常需要验证嵌套对象的数据。通过使用 Chai 中的 "own" 和 "include" 断言方...

    1 年前
  • Promise VS Observable 在异步编程时的选择

    当执行异步操作时,Promise 和 Observable 是常用的两种技术,这两者有什么区别和优缺点?在不同的情况下该如何选择?本文将详细介绍。 Promise Promise 是一种JS标准API...

    1 年前
  • Serverless 如何进行耐久化日志记录

    Serverless 技术越来越流行,但是大多数 Serverless 服务提供商并没有提供完整的日志记录功能。这就需要我们在应用程序中进行耐久化日志记录,以便更好地理解应用程序的行为。

    1 年前
  • Material Design 中如何使用 CardView 控件创建带有阴影效果的卡片

    在 Material Design 中,CardView 是一种常用的控件,它能够为应用程序提供一个漂亮而统一的外观,并且可以方便地创建带有阴影效果的卡片。在本文中,我们将介绍如何使用 CardVie...

    1 年前
  • Docker 容器中配置 HTTPS 的方法

    概述 使用 HTTPS 加密协议,可以确保应用程序在传输过程中的数据安全性。在 Docker 容器中配置 HTTPS 协议,可以提高 Web 应用程序的安全性,本文将介绍如何在 Docker 容器中配...

    1 年前
  • ECMAScript 2018 (ES9) 中的新特性:RegExp Lookbehind Assertions

    在 ECMAScript 2018 (ES9) 中,新增了一种正则表达式的特性—— Lookbehind Assertions(后行断言),可以用于匹配某个位置之前的文本部分,这种特性在某些场景下可以...

    1 年前
  • 使用 LESS 开发高效的 CSS 解决方案

    随着前端技术的发展,CSS 已经成为了前端开发中不可或缺的一部分。然而,CSS 的书写方式相对传统的编程语言而言更为简单,这也容易导致开发过程中出现代码无序且臃肿的问题。

    1 年前

相关推荐

    暂无文章