JavaScript 中的 Promise 是一种非常重要的异步编程方式,它能够在异步方法执行完毕后通知调用方,使得异步编程变得更加简单和可读。然而,当我们需要同时执行多个异步方法,并在它们全部执行完毕后执行另一个方法时,我们就需要使用 Promise 并行调用。
在这篇文章中,我们将探讨如何实现高性能的 Promise 并行调用,并讨论这种技术的深度和学习以及指导意义。
问题与挑战
在进行异步编程时,我们常常会需要同时调用多个异步方法,以达到更好的性能和并行度。例如,我们可能需要同时请求多个 API 接口,并在它们全部请求完毕后,将结果合并并进行下一步处理。
这时,我们就需要使用 Promise 并行调用。
Promise 并行调用的实现其实并不复杂。我们只需要将所有需要同时执行的异步方法用 Promise.all 或 Promise.race 封装一下即可。例如:
-- -------------------- ---- ------- ------------- ------- ------- ------- -- ---------------- -------- --------- -- - -- ---- -- ---------- -- - -- ---- --
然而,这种简单的实现方式也存在着一些问题和挑战。
首先,当同时请求的异步方法非常多时,Promise.all 或 Promise.race 的性能可能会受到影响,从而导致程序的运行速度变慢。
其次,当请求的异步方法存在依赖关系时,我们需要手动进行一些调度和等待。例如,我们需要先请求 api1,然后根据 api1 的结果请求 api2 和 api3:
-- -------------------- ---- -------
------
------------- -- -
------ -------------
-------------------------
--------------
--------------
--
--
---------------- -------- --------- -- -
-- ----
--
---------- -- -
-- ----
--以上代码中,我们需要手动地创建一个 Promise 实例作为 api1 的结果,然后将它与 api2 和 api3 的 Promise 实例一起传递给 Promise.all。这种方式虽然可行,但是不太优雅,而且容易出错。
高性能的 Promise 并行调用
为了解决以上问题和挑战,我们可以使用另一种实现方式:通过创建一个自定义的 Promise 类,将异步方法的执行和结果处理逻辑封装在一起,并实现一个高性能的 Promise 并行调用库。
我们可以首先定义一个 AsyncPromise 类,用来包装一个异步方法和它的执行逻辑:
-- -------------------- ---- -------
----- ------------ -
--------------------- -
------------- - ---------
------------ - ----------
------------- - -- -- ---
-
--------- -
-- --------------- -
--- -
------------ - ----------------
--------------------- -- -
------------------- -----
------------ -- -
------------------ ------
---
- ----- ----- -
------------------ ------
-
-
------ -------------
-
-------------- -
----- ------- - --- --------------- -- -
------ ----------------------- -- -
------ --------------
---
---
---------------- - --------------
------ --------
-
--------------- -
----- ------- - --- --------------- -- -
------ ------------------------ -- -
------ --------------
---
---
---------------- - --------------
------ --------
-
-以上代码中,AsyncPromise 类包含了异步方法的执行和结果处理逻辑。它的构造函数接受一个 promiser 函数,该函数返回一个 Promise 实例。它的 execute 方法用于执行异步方法并返回一个 Promise 实例。如果该异步方法已经被执行过了,就不会重复执行,并且直接返回之前的 Promise 实例。它的 then 和 catch 方法可以链式调用,用于添加回调函数。
接下来,我们可以定义一个 Parallel 类,用来实现高性能的 Promise 并行调用:
-- -------------------- ---- -------
----- -------- -
------ ----- - ---
------ ----- - ---
------ ------------ - --
------ --------------------- -
----------------------------------
-- ---------------------- - --------------- -
------ --- --------------- -- -
----- ---- - -- -- -
-- ---------------------- - --------------- -
----- ------------ - -----------------------
-- -------------- -
--------------------- -- --
------------------------------ -- -
--------------------- -- --
-------
---
- ---- -
----------
-
-
--
-------
---
-
------ ------------------
-
------ ------------------ -
-- ------------------------------- -
------------- - ----------------
-
------ --- ----------------- ------- -- -
----- ------- - ---
--- ------------- - --
------------------------------------ ------ -- -
--------------------- -- -
-------------- - ----
------------- -- --
-- -------------- --- --------------------- -
-----------------
-
------------ -- -
------------
---
-------------------------------
---
---
-
------ ------------------- -
-- ------------------------------- -
------------- - ----------------
-
------ --- ----------------- ------- -- -
--- -------- - ------
--- -------- - ------
---------------------------------- -- -
--------------------- -- -
-- ---------- -- ---------- -
-------- - -----
-------------
-
------------ -- -
-- ---------- -- ---------- -
-------- - -----
------------
-
---
-------------------------------
---
---
-
-以上代码中,Parallel 类包含了两个静态方法:all 和 race。它们分别用于实现 Promise.all 和 Promise.race 的功能。
在 Parallel 类中,我们使用 limit 和 queue 两个静态变量来限制并发数量,并且使用 runningCount 变量来记录当前正在执行的异步方法数。
在 Parallel 的执行逻辑中,我们通过 enqueue 方法来将异步方法添加到队列中。该方法会根据当前并发数量来决定是否需要等待之前的异步方法执行完毕。当队列中的异步方法数量超过限制时,我们就需要进行等待。
在执行 all 和 race 方法时,我们在异步方法链条上添加一个 Promise 实例,用来包装异步方法的执行和结果处理逻辑。然后,我们将该 Promise 实例添加到队列中,并在异步方法执行完毕后,将其从队列中移除。
示例代码
以下代码演示了如何使用 Parallel 类来实现高性能的 Promise 并行调用:
-- -------------------- ---- -------
----- ---- - --- --------------- -- -
------ --- --------------- -- -
------------- -- -
-----------
-- ------
---
---
----- ---- - --- --------------- -- -
------ --- --------------- -- -
------------- -- -
-----------
-- ------
---
---
----- ---- - --- --------------- -- -
------ --- --------------- -- -
------------- -- -
-----------
-- ------
---
---
------------------- ----- ------------------- -- -
---------------------
------------ -- -
-------------------
---在以上代码中,我们定义了三个异步方法 api1、api2 和 api3,并使用 AsyncPromise 类将它们封装起来。然后,我们使用 Parallel.all 方法来并行调用它们,并在所有异步方法执行完毕后输出结果。通过限制并发数量和优化执行逻辑,我们能够实现高性能的 Promise 并行调用,并且保持代码的简洁和可读性。
学习和指导意义
本文介绍了如何实现高性能的 Promise 并行调用,并且讨论了该技术的深度和学习以及指导意义。
通过本文的介绍,我们了解到了并发编程的重要性,并掌握了一种优雅而高效的实现方式。同时,我们也学习了如何使用 AsyncPromise 和 Parallel 类来封装异步方法的执行和结果处理逻辑,以达到更好的可读性和维护性。
更重要的是,通过本文的学习,我们可以更好地理解 Promise 的工作原理和编程范式,并且能够更加熟练地运用异步编程技术来解决实际问题。
Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/67933253504e4ea9bd7514a0