Promise 实践:实现 JavaScript 中的并发控制

Promise 是 JavaScript 中处理异步操作的一种规范,可以方便地管理异步操作,减少回调嵌套。除此之外,Promise 还可以实现并发控制,在一定程度上提高了代码的性能和可维护性。

本篇文章将讲解 Promise 在并发控制中的应用,包括如何使用 Promise.all 和 Promise.race 方法进行多个异步操作的批量处理和竞争处理,以及如何实现一个自定义的并发控制器。

Promise.all

Promise.all 接收一个 Promise 对象数组作为参数,返回一个新的 Promise 对象。当数组中所有 Promise 对象都执行成功后,返回的 Promise 对象才执行成功,同时将每个 Promise 对象的返回值组成数组作为成功的结果。若数组中有一个 Promise 对象执行失败,则立即返回一个失败的 Promise 对象,并抛出第一个被拒绝的 Promise 对象的错误信息。

下面是一个例子,展示如何使用 Promise.all 方法批量处理多个 URL 请求:

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

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

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

在上面的例子中,我们首先定义了一个数组 urls,其中包含了需要请求的多个 URL。我们在 Promise.all 方法中传入一个将 urls 数组中的每个 URL 转化为一个返回 Promise 的 fetch 函数的 map 方法,并将这个数组传递给 Promise.all 方法。如果所有的 fetch 请求执行成功,then 语句将会被执行,并且我们可以通过 responses 数组访问到所有请求的结果。否则,catch 语句将会被执行,我们可以在这里处理错误信息。

Promise.race

Promise.race 接收一个 Promise 对象数组作为参数,返回一个新的 Promise 对象。与 Promise.all 不同,Promise.race 只要求数组中的一个 Promise 对象执行成功或失败即可。当数组中有一个 Promise 对象执行成功或失败后,返回的 Promise 对象即执行成功或失败,并返回第一个完成的 Promise 对象的结果或错误信息。

下面是一个例子,展示如何使用 Promise.race 方法竞争处理多个相似的请求,只需保留最快的结果即可:

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

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

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

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

在上面的例子中,我们首先定义了一个数组 urls,其中包含了需要请求的多个 URL。我们将 urls 数组使用 map 方法转化为多个返回 Promise 的 fetch 函数,并将这些 Promise 对象保存在 promises 数组中。然后我们使用 Promise.race 方法,并传入 promises 数组作为参数。然后我们在 then 语句中访问最快的 Promise 对象的结果。如果第一个 Promise 对象执行失败,则 catch 语句将执行,并处理错误信息。

自定义并发控制器

虽然 Promise.all 和 Promise.race 提供了批量处理多个异步操作的机制,但是往往只能满足简单的应用场景。对于更复杂的需求,我们可能需要自定义一个并发控制器来对异步操作进行更精细的管理。

下面是一个例子,展示如何实现一个带有并发限制的 Promise 并发控制器,该并发控制器可以同时处理多个 Promise,但限制了同时处理的 Promise 数量。使用该控制器,我们可以避免一次性使用大量内存和处理器资源。

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

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

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

在上面的代码中,我们首先定义了一个 PromiseLimit 类,该类的构造函数接收一个并发限制参数 limit,表示同时最多允许处理几个 Promise 对象。然后我们定义了三个实例变量:running 表示正在运行的 Promise 数量;queue 表示等待运行的 Promise 队列;limit 表示并发限制数。

我们在该类中定义了 start 方法,该方法接收一个函数 task,并且返回一个新的 Promise 对象。该方法的作用是将 task 函数放入队列并进行异步执行,并且当执行完成后,返回 task 函数的结果。

我们在 start 方法中定义了一个 runTask 异步函数,用于异步执行 task 函数,并且在执行成功后调用 resolve 方法返回 task 函数的结果。在执行失败时,我们调用 reject 方法抛出异常。在这个异步函数最后,我们将 running 值减一,并尝试执行下一个队列中的任务。

自定义的并发控制器在 runNext 方法中实现。该方法执行一个循环,用于检查是否有空闲的运行时,如果有,则从等待队列中取出一个任务并运行它。在运行任务时,running 数量加 1。

下面是一个使用自定义 Promise 并发控制器的例子:

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

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

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

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

在上面的例子中,我们首先定义了一个 delay 函数,用于暂停一定时间,以便随后的任务处理。然后我们实例化了一个 PromiseLimit 对象,并设置并发限制为 2。

我们定义了四个 task 函数,每个函数都使用 delay 函数模拟异步操作,并在操作完成后打印一条信息。然后我们依次调用 promiseLimit.start 方法,并传入每个 task 函数。由于并发限制为 2,最多只能同时处理两个 task 函数。因此,前两个任务会立即运行,而后两个任务会等待前两个任务执行完毕。在开始并发控制后,程序会不停地打印下面的信息:

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

在上面的例子中,我们演示了如何使用自定义的并发控制器,使得异步操作可以更灵活地管理和控制。通过异步编程技术的结合,我们可以在进行大量异步处理的同时,还可以避免一次性耗尽大量内存和处理器资源。

结论

Promise 是 JavaScript 中处理异步操作的一种规范,它可以方便地管理异步操作,减少回调嵌套。除此之外,Promise 还可以实现并发控制,在一定程度上提高了代码的性能和可维护性。

在本篇文章中,我们讲解了 Promise 在并发控制中的应用,包括使用 Promise.all 和 Promise.race 方法进行多个异步操作的批量处理和竞争处理,以及如何实现一个自定义的并发控制器。希望这篇文章可以帮助您更好地理解 Promise,以便更加高效地进行异步编程。

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