解决 JavaScript 中 Promise 的 Cancelled 问题

阅读时长 9 分钟读完

在 JavaScript 中,Promise 是一个非常常用的异步编程模式。但是在实际使用过程中,我们经常会遇到一个问题:如何取消一个 Promise?

在许多情况下,我们可能需要在 Promise 还没有完成之前取消它。比如用户在操作过程中取消了一个请求,或者在页面切换时需要取消正在进行的一些异步操作等等。

本文将介绍如何解决 JavaScript 中 Promise 的 Cancelled 问题,并提供一些示例代码供大家参考。

Promise 的基本用法

在开始讲解如何取消 Promise 之前,我们先来回顾一下 Promise 的基本用法。

Promise 是一个异步操作的容器,它可以将异步操作封装成一个对象,使得我们能够更方便地进行异步编程。一个 Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

我们可以通过 Promise 的 then 方法来注册成功回调函数,通过 catch 方法来注册失败回调函数。当 Promise 的状态发生变化时,对应的回调函数就会被调用。

下面是一个简单的示例代码:

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

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

在这个示例中,我们定义了一个 fetch 函数,它返回一个 Promise 对象。在 Promise 对象的构造函数中,我们使用 XMLHttpRequest 发送了一个 GET 请求,并在请求完成后根据响应状态来决定 Promise 的状态。如果请求成功,我们调用 resolve 方法将 Promise 的状态设置为 fulfilled,并将响应数据作为参数传递给成功回调函数;如果请求失败,我们调用 reject 方法将 Promise 的状态设置为 rejected,并将错误信息作为参数传递给失败回调函数。

在使用 fetch 函数时,我们可以通过 then 方法来注册成功回调函数,通过 catch 方法来注册失败回调函数。当 Promise 对象的状态发生变化时,对应的回调函数就会被调用。

如何取消 Promise

在实际使用 Promise 的过程中,我们可能需要在 Promise 还没有完成之前取消它。比如在用户操作过程中取消一个请求,或者在页面切换时需要取消正在进行的一些异步操作等等。

那么,如何取消 Promise 呢?

在 JavaScript 中,Promise 并没有提供一个标准的取消方法。但是我们可以通过一些技巧来实现 Promise 的取消。

方法一:使用 AbortController

AbortController 是一个新的 Web API,它可以用来取消 Fetch 请求、Web Socket 连接等等。我们可以利用 AbortController 来实现 Promise 的取消。

首先,我们需要在 Promise 的构造函数中传入一个 AbortController 对象,并将其保存在 Promise 实例中。然后,在需要取消 Promise 的时候,我们可以调用 AbortController 的 abort 方法来取消 Promise。

下面是一个使用 AbortController 实现 Promise 取消的示例代码:

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

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

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

在这个示例中,我们在 Promise 的构造函数中创建了一个 AbortController 对象,并将其保存在 Promise 实例中。然后,在发送请求时,我们将 AbortController 的 signal 属性作为 XMLHttpRequest 的 abort 方法的参数,这样当 AbortController 调用 abort 方法时,XMLHttpRequest 也会被取消。

同时,我们还通过 signal 的 addEventListener 方法注册了一个 abort 事件的回调函数,在 Promise 被取消时会调用该回调函数,并在回调函数中调用 reject 方法将 Promise 的状态设置为 rejected。

最后,我们在 setTimeout 中调用了 promise.controller.abort 方法来取消 Promise。

方法二:使用 race 方法

另一种实现 Promise 取消的方法是使用 Promise 的 race 方法。

Promise 的 race 方法可以接受一个 Promise 数组作为参数,并返回一个新的 Promise 对象。当 Promise 数组中的任意一个 Promise 对象的状态发生变化时,返回的 Promise 对象就会跟着变化。

我们可以利用这个特性来实现 Promise 的取消。具体来说,我们可以将一个 Promise 对象和一个 Promise.race([Promise.resolve(), cancelPromise]) 的结果一起放在 Promise.all([promise, Promise.race([Promise.resolve(), cancelPromise])]) 中,这样当 cancelPromise 被 resolve 时,Promise.all 返回的 Promise 对象就会被取消。

下面是一个使用 race 方法实现 Promise 取消的示例代码:

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

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

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

在这个示例中,我们首先定义了一个 cancelPromise,它会在 1 秒后被 reject。然后,在 fetch 函数中,我们将 promise 和 Promise.race([Promise.resolve(), cancelPromise]) 的结果一起放在 Promise.all([promise, Promise.race([Promise.resolve(), cancelPromise])]) 中,这样当 cancelPromise 被 reject 时,Promise.all 返回的 Promise 对象就会被取消。

最后,我们在 setTimeout 中调用了 promise.catch(() => {}),这样就可以忽略掉 Promise 的取消异常,避免在控制台中出现错误信息。

总结

在 JavaScript 中,Promise 是一个非常常用的异步编程模式。但是在实际使用过程中,我们经常会遇到一个问题:如何取消一个 Promise?

在本文中,我们介绍了两种实现 Promise 取消的方法:使用 AbortController 和使用 race 方法。这些方法虽然不是标准的 Promise API,但是它们可以让我们更方便地进行异步编程,提升代码的可读性和可维护性。

在使用这些方法时,我们需要注意一些细节,比如避免在控制台中出现取消异常等等。但是只要我们掌握了这些技巧,就可以更加自如地使用 Promise,写出更加优雅和高效的异步代码。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/65bc8533add4f0e0ff524100

纠错
反馈