如何解决 Promise.all 中有 Promise.reject 时如何中断 Promise.all

Promise.all 是 Promise 中的一种组合方式,我们可以使用它来在多个异步任务并行执行的情况下获取它们的返回结果,并在所有任务完成后进行后续操作。但是,当 Promise.all 中存在 Promise.reject 的时候,我们需要如何中断 Promise.all 呢?这篇文章将会详细介绍中断 Promise.all 的方法和原理。

Promise.all 基本使用方法

Promise.all 接收一个 promise 数组作为参数,返回一个新的 promise,只有当所有的 promise 都完成时,新的 promise 才会完成。同时,新的 promise 成功返回的数据会以一个数组的形式返回所有 promise 的结果。

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

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

Promise.all 中存在 Promise.reject

当我们在 Promise.all 中使用的 promise 数组中存在 Promise.reject 的时候,Promise.all 将会直接忽略这个错误。如下面的代码,Promise.all 中有一个 promise 是 Promise.reject,但是最后的结果却只有 [1]。

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

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

这是因为 Promise.all 是将所有的 promise 任务都视为一个整体来执行的。只要有一个任务返回了错误,整个任务就会失败,但不会中断。

如何中断 Promise.all

在实际开发中,我们可能会遇到这样的情况:我们需要在某个任务异常的时候中断 Promise.all 的执行,而不是忽略这个错误。例如,我们需要从多个后端接口中获取数据,当其中一个接口返回错误时,我们希望中止所有接口的请求。

解决方法是我们可以手动设置一个标志位或者使用一个累加器,用来判断并记录任务是否出现异常。当然也可以使用第三方的扩展库,例如 promise-any-racepromise-some 等。下面是一个手动设置标志位的示例代码:

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

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

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

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

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

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

在这个示例代码中,我们使用一个名为 breakable 的变量来表示是否需要中断 Promise.all 的执行。如果 breakable 变量被设置为 true,那么 catch 函数将会抛出一个错误,并终止所有 promise 的执行。当某个 promise 任务抛出错误时,整个任务中止,后续的 promise 任务都不会被执行。

深度解析

在我们了解了如何中断 Promsie.all 的执行之后,让我们来看看其中的原理。

Promise 如何维持所有 promise 的结果

在前面的基本使用中,我们介绍了 Promise.all 的一个特性,它会将所有的 promise 任务都视为一个整体来执行。

在 Promise.all 内部,它会创建一个空数组 values,然后通过 for...of 循环迭代 iterable 中的所有 promise,进行如下操作:

  • 如果迭代的当前值不是 promise,就将其添加到 values 数组中。
  • 如果迭代的当前值是 promise,那么会通过其 then 方法,把 resolveValue 添加到 values 数组中。

所有的 promise 任务都在 Promise.all 的 for 循环中被处理。在循环结束之后,Promise.all 会给这些 Promise 添加一个 resolve 或 reject 处理方法。在这些处理方法中,Promise.all 会判断每个 Promise 的状态(fulfilled 或 rejected)。如果所有 Promise 都是 fulfilled,Promise.all 将会返回一个包含所有返回值的数组。如果至少有一个 Promise 是 rejected,Promise.all 将会返回最先被 rejected 的 Promise 的错误。

Promise 中断的处理

在 Promise 中断的处理过程中,我们在 catch 处理函数中显式地抛出一个异常,Promise 会调用这个异常对象的 reject 方法,以便终止整个任务的执行。在 Promise 操作的任务队列中,此时最新的 rejected Promise 已成为了任务队列中的一员,并且已遵循了其被 rejected 的路径,将会作为任务队列中的下一个 Promise。因此,后续的 Promise 任务都不会得到执行。

结论

当 Promise.all 中存在 Promise.reject,我们无法通过 Promise.all 中断所有任务的执行。手动设置标志位或使用第三方库来实现 Promise 中断的方法都是可行的,但是需要注意,在多并发的场景下,中断了正在运行的 Promise 可能会对系统造成一定的影响和损失。

参考

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/67187c06ad1e889fe22bc351