Promise 中如何取消一个未完成的异步操作

阅读时长 7 分钟读完

在前端开发中,我们经常需要进行异步操作,比如使用 Ajax 请求 API 数据、上传文件、加载图片等等。Promise 是一种用来处理异步操作的对象,它可以让我们更方便地管理异步代码,避免回调函数嵌套的问题。但是,在某些情况下,我们可能需要取消一个未完成的异步操作。本文将介绍在 Promise 中如何实现取消异步操作,帮助读者更好地管理异步代码。

什么是 Promise

Promise 是一个对象,用来表示一个异步操作的最终完成或失败,以及其返回的值。Promise 有三种状态:pending、fulfilled 和 rejected。当一个 Promise 对象被创建时,它处于 pending 状态,这意味着异步操作尚未完成。当异步操作成功完成时,Promise 对象会转换为 fulfilled 状态,并返回值。而当异步操作失败时,Promise 对象会转换为 rejected 状态,并返回错误信息。

Promise 有两个重要的方法:then() 和 catch()。当 Promise 对象从 pending 状态转换为 fulfilled 状态时,then() 方法会被调用,catch() 方法则会在 Promise 对象转换为 rejected 状态时被调用。

下面是一个简单的 Promise 示例:

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

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

这个 Promise 对象表示一个异步操作,它会在 1 秒钟后成功地返回字符串 "Hello Promise"。在 then() 方法中,我们打印了这个字符串。如果异步操作发生错误,则会在 catch() 方法中输出错误信息。

如何取消 Promise

在某些情况下,我们可能需要取消一个未完成的异步操作。比如,当用户在正在上传文件时取消上传操作,或者在加载图片时用户离开了当前页面,我们希望可以取消未完成的异步操作,以提高用户体验。但是,在 Promise 中,取消异步操作并不是一个简单的任务,因为 Promise 的设计初衷是一旦创建就不能取消。然而,有一些方法可以实现类似的效果。

方法一:使用 race()

Promise.race() 方法可以接受一组 Promise 对象,并返回一个新的 Promise 对象。这个新的 Promise 对象会等待任一一个 Promise 对象完成,并返回它的值。因此,我们可以将要执行的异步操作和一个 Promise.race() 对象一起放在一个集合中,通过取消 Promise.race() 对象来取消异步操作。

下面是一个使用 Promise.race() 方法取消异步操作的示例:

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

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

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

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

在这个示例中,我们使用 XMLHttpRequest 发起了一个 GET 请求,获取了一个用户列表。我们创建了一个 cancelPromise,它会在 500 毫秒后返回 "Operation canceled"。然后,我们将 promise 对象和 cancelPromise 对象一起传递给 Promise.race() 方法,并将结果保存在 promise 对象中。这样,如果异步操作在 500 毫秒内未完成,就会返回 "Operation canceled"。如果异步操作成功完成,则会返回用户列表。

如果要取消异步操作,我们只需要将 cancelPromise 对象从 promise 数组中移除,这样就可以取消异步操作。不过需要注意的是,这种方法只能取消尚未完成的异步操作。如果异步操作已经完成,就无法取消。

方法二:使用 throw error

另一种取消 Promise 的方法是,在 promise 的回调函数中抛出一个错误。这个错误会导致 promise 对象转换为 rejected 状态,并传递给 catch() 方法。如果我们在 catch() 方法中处理这个错误,就可以实现取消异步操作的效果。这种方法的优点是,在异步操作完成之前可以随时取消,但是需要在异步操作的回调函数中手动抛出错误。

下面是一个使用 throw error 方法取消异步操作的示例:

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

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

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

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

在这个示例中,我们使用 XMLHttpRequest 发起了一个 GET 请求,加载了一张图片。我们使用一个 setTimeout() 函数,500 毫秒之后取消了异步操作,同时在回调函数中抛出一个带有 "Canceled" 错误消息的错误。我们在 then() 方法中打印了图片的内容,如果图片加载失败则会在 catch() 方法中输出错误信息。

在 catch() 方法中,如果捕获到的错误消息是 "Canceled",就表示异步操作已经被取消了。这时,我们可以输出 "Operation canceled!"。如果错误消息不是 "Canceled",就表示异步操作出现了其他错误,这时我们输出错误信息。

需要注意的是,在 then() 方法中必须忽略本次操作的异常。否则,如果异步操作完成之后抛出了异常,catch() 方法就会捕获这个异常,而我们希望在异步操作取消之后不再执行 then() 方法。

结论

虽然 Promise 的设计初衷是一旦创建就不能取消,但是我们可以通过一些方法实现类似的效果。这些方法可以帮助我们在必要时取消异步操作,以提高用户体验。本文介绍了两种常用的方法:使用 Promise.race() 方法和在异步操作的回调函数中抛出错误。需要注意的是,这些方法都有其优缺点,具体使用方法需要根据实际情况来选择。

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

纠错
反馈