Promise Pool JavaScript LeetCode 2636
Tags: javascript, leetcode, frontend, interview
Published: 2023-08-15 15:42:43

Given an array of asynchronous functions `functions` and a `pool limit n`, return an asynchronous function `promisePool`. It should return a promise that resolves when all the input functions resolve.

The `Pool limit` is defined as the maximum number of `promises` that can be pending at once. `promisePool` should begin execution of as many functions as possible and continue executing new functions when old promises resolve. promisePool should execute `functions[i]` then `functions[i + 1]` then `functions[i+2]`, etc. When the last promise resolves, `promisePool` should also resolve.

For example, if `n = 1`, promisePool will execute one function at a time in a series. However, if `n = 2`, it first executes two functions. When either of the two functions resolves, a 3rd function should be executed (if available), and so on until there are no functions left to execute.
You can assume all functions never reject. It is acceptable for promisePool to return a promise that resolves any value.

## Example 1:
**Input:**

```
functions = [
    () => new Promise(res => setTimeout(res, 300)),
    () => new Promise(res => setTimeout(res, 400)), 
    () => new Promise(res => setTimeout(res, 200))
]
n = 2
```
**Output:** [[300, 400, 500],500]

**Explanation:**

Three functions are passed in. They sleep for 300ms, 400ms, and 200ms respectively.
They resolve at 300ms, 400ms, and 500ms respectively. The returned promise resolves at 500ms.
At t=0, the first 2 functions are executed. The pool size limit of 2 is reached.
At t=300, the 1st function resolves, and the 3rd function is executed. The pool size is 2.
At t=400, the 2nd function resolves. There is nothing left to execute. The pool size is 1.
At t=500, the 3rd function resolves. The pool size is 0 so the returned promise also resolves.

## Example 2:
**Input:**

```
functions = [
    () => new Promise(res => setTimeout(res, 300)),
    () => new Promise(res => setTimeout(res, 400)),
    () => new Promise(res => setTimeout(res, 200))
] 
n = 5
```
**Output:** [[300, 400, 200], 400]

**Explanation:**

The three input promises resolve at 300ms, 400ms, and 200ms respectively.
The returned promise resolves at 400ms.
At t=0, all 3 functions are executed. The pool limit of 5 is never met.
At t=200, the 3rd function resolves. The pool size is 2.
At t=300, the 1st function resolved. The pool size is 1.
At t=400, the 2nd function resolves. The pool size is 0, so the returned promise also resolves.

## Example 3:

**Input:**

```
functions = [
    () => new Promise(res => setTimeout(res, 300)),
    () => new Promise(res => setTimeout(res, 400)),
    () => new Promise(res => setTimeout(res, 200))
] 
n = 1
```
**Output:** [[300,700,9001,900]

**Explanation:**

The three input promises resolve at 300ms, 700ms, and 900ms respectively. The returned promise resolves at 900ms.
At t=0, the 1st function is executed. The pool size is 1.
At t=300, the 1st function resolves and the 2nd function is executed. The pool size is 1.
At t=700, the 2nd function resolves and the 3rd function is executed. The pool size is 1.
At t=900, the 3rd function resolves. The pool size is 0 so the returned promise resolves.

## Constraints:

`0 <= function.length <= 10`
`0 <= n <= 10`

## Intuition:
The problem revolves around executing a given set of asynchronous functions in a controlled manner with a specified pool limit. The goal is to execute as many functions concurrently as the pool limit allows while ensuring that the overall execution sequence follows the order of the input functions.

## Approach:

1. The promisePool function is defined with two parameters: an array of asynchronous functions functions and an integer n representing the pool limit.

2. The function returns a Promise. It's an asynchronous function that takes advantage of the async/await syntax and uses Promise for its asynchronous operations.

3. Inside the returned Promise, a helper function is defined. This function is responsible for managing the execution of functions and the pool size.

4. The base case of the recursion is checked: If the index is greater than or equal to the length of functions and there are no promises in progress (inProgress === 0), it means all functions have been executed. In this case, the main promise is resolved to signal the completion.

5. Inside the helper function, a while loop is used to execute functions in parallel up to the pool limit n. It checks if there's room in the pool (inProgress < n) and if there are more functions to execute (index < functions.length).

6. When the loop condition is met, the inProgress counter is incremented, and the current function at the index is executed using .then(). When the promise of the function resolves, the inProgress counter is decremented, and the helper function is called recursively. This ensures that as soon as one function completes, another function is started.

7. Outside the helper function, the execution is initiated by calling the helper function for the first time.

## Code:

```javascript
/**
 * @param {Function[]} functions
 * @param {number} n
 * @return {Function}
 */
// TC: O(functions.length)
var promisePool = async function (functions, n) {
  return new Promise((resolve) => {
    let inProgress = 0, index = 0;
    function helper() {
      // base case
      if (index >= functions.length) {
        if (inProgress === 0) resolve();
        return;
      }

      while (inProgress < n && index < functions.length) {
        inProgress++;
        functions[index++]().then(() => {
          inProgress--;
          helper();
        });
      }
    }
    helper();
  });
};

/**
 * const sleep = (t) => new Promise(res => setTimeout(res, t));
 * promisePool([() => sleep(500), () => sleep(400)], 1)
 *   .then(console.log) // After 900ms
 */
```

## Time Complexity:

The time complexity of this code is **`O(functions.length)`**, where "functions.length" represents the number of functions in the input array. This is because each function is executed once within the while loop, and the while loop iterates through all the functions in the array.

## Space Complexity:

The space complexity of this code is **`O(1)`**, which is constant space complexity. This is because the memory usage of the code does not grow with the input size or any recursion depth.

## Checkout the video Solution:
{% embed https://www.youtube.com/watch?v=gsLvpzuheQU %}

Hope you find helpful, if yes then give it a like.