理解 Promise 无效的事实(并将其纠正)

阅读时长 7 分钟读完

前言

随着 JavaScript 语言的发展,对异步编程变得越来越重要。在过去,我们使用回调函数来处理异步操作,但是这种方式很快就被证明是不可取的,因为它很容易导致回调地狱。Promise 出现了,它是一种更好的处理异步操作的方式。但是,在实践中,我们经常会犯一些错误,这些错误使得我们无法充分发挥 Promise 的优势。本文将介绍一些常见的 Promise 误解,并对这些误解进行纠正。

Promise 的基本概念

Promise 本质上是一个占位符对象,可以代表一个异步操作的最终结果。一个 Promise 实例有三种状态:

  • pending:初始状态,即等待异步操作的完成。
  • fulfilled:异步操作已经完成,且成功完成。
  • rejected:异步操作已经完成,但是执行失败。

当一个 Promise 实例处于 fulfilled 或者 rejected 状态时,它被认为是 settled(已定型的)。在 settled 状态下,一个 Promise 实例不能再改变状态。

Promise 的误解

在使用 Promise 的过程中,很容易犯一些错误。下面是一些常见的 Promise 误解。

1. 传递参数

我们经常会写出下面这样的代码:

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

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

然而,这个代码在处理异步操作的结果时很容易出错。如果异步操作的结果是一个对象,我们可能会写出下面这样的代码:

但是,如果异步操作执行失败,会发生什么呢?代码会抛出一个错误。因此,我们应该使用 catch() 方法来处理 Promise 实例的 rejected 状态。

这种情况下,我们通常会以如下形式传递异步操作的结果:

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

2. 串行执行异步操作

使用 Promise 可以轻松地编写串行执行的异步操作代码。例如,我们可以通过返回一个 Promise 实例来实现这个需求。假设我们有一个需要两个异步操作的函数,第一个异步操作返回一个字符串,第二个异步操作需要这个字符串作为参数。代码如下:

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

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

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

这段代码执行了两个异步操作,不需要传递任何状态。这种方式使用起来很方便,但是在实践中,它可能会出问题。如果其中一个异步操作失败了,Promise 实例就会处于 rejected 状态,我们就无法进一步处理。

例如,假设我们有一个下面这样的异步操作:

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

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

如果我们按照上面的方式编写代码,我们无法继续执行其它异步操作。为了解决这个问题,我们应该使用 catch() 方法来处理异常情况。

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

这段代码将在任何情况下都能处理异步操作的结果,并在结束时打印一条消息。

3. 误解 Promise.all()

在使用 Promise.all() 方法时,我们需要注意失败处理。Promise.all() 方法接收一个 Promise 实例数组,然后将它们并列地执行。如果所有 Promise 实例都成功,Promise.all() 方法就会返回解决的 Promise 实例。否则,它将返回一个拒绝的 Promise 实例。例如,下面是一个正确使用 Promise.all() 方法的示例代码:

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

在这个例子中,我们忽略了每个异步操作的状态。换句话说,我们假设每个异步操作都将成功完成。然而,在实践中,这种假设是不合理的。如果假设失败的异步操作将永远不会发生,那么代码将无法诊断错误。

因此,我们应该像这样处理状态:

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

这个示例代码告诉我们,如果有一个异步操作失败了,Promise.all() 方法就会停止执行,并返回一个拒绝的 Promise 实例。我们应该使用 catch() 方法来处理失败情况,并使用 finally() 方法来结束 Promise 实例的执行。

结论

本文介绍了 Promise 的一些基本概念,以及 Promise 使用中容易出现的一些错误。在使用 Promise 时,我们应该更加注意 Promise 实例的状态,以及一些 Promise 方法的使用细节。对于每一个 Promise 实例,我们应该总是提供一个 catch() 方法,以处理 Promise 实例的 rejected 状态。在同时执行多个异步操作(例如,使用 Promise.all() 方法)时,我们应该总是注意异步操作可能失败的情况,并提供合适的失败处理。

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

纠错
反馈