2721. Execute Asynchronous Functions in Parallel

šŸš€ Implementing Promise.all() in JavaScript

Problem Statement

In JavaScript, Promise.all() is a powerful method that takes an array of promises and returns a single promise. This new promise:

  • Resolves to an array containing the results of each input promise (in the original order).
  • Rejects immediately if any one of the input promises rejects.

For this challenge, we are tasked with implementing our own version of Promise.all() from scratch.

šŸ”— My LeetCode Solution: Implementing Promise.all in JavaScript


🧠 Intuition

The essence of Promise.all() is that:

  1. It must wait for all promises to resolve before returning an array of results.
  2. It must return the results in the same order as the input array, even if promises resolve out of order.
  3. If any promise rejects, it should immediately reject without waiting for the others.

To achieve this behavior:

  • We’ll need to track how many promises have resolved.
  • Store the resolved values at the correct indices.
  • Handle any rejection by immediately rejecting the main promise.

šŸ“ Approach

We'll break down our solution step-by-step:

  1. Create a new promise that resolves once all input promises are fulfilled, or rejects if any promise fails.
  2. Initialize an array to store the results, ensuring they appear in the original order.
  3. Track the number of remaining promises that need to resolve.
  4. Loop through the input functions:
    • Invoke each function to get its promise.
    • Attach .then() to handle successful resolution and store the result.
    • Attach .catch() to handle rejection and immediately reject the main promise.
  5. If all promises resolve, resolve the main promise with the results array.

šŸ’» Solution Code

Here’s the implementation:

var promiseAll = function(functions) {

    return new Promise((resolve, reject) => {

        let result = Array(functions.length);  // Array to store results

        let totalFunction = functions.length;  // Counter to track pending promises


        functions.forEach((fn, index) => {

            fn()

                .then((res) => {

                    result[index] = res; // Store result at correct index

                    totalFunction--;     // Decrease counter

                    if (totalFunction === 0) resolve(result); // If all resolved, return result

                })

                .catch(reject); // Reject if any promise fails

        });

    });

};


šŸ” Complexity Analysis

Time Complexity: O(n)

  • The function iterates over all the input functions once, resulting in a linear time complexity of O(n).
  • Each individual promise may take different amounts of time to resolve, but our solution’s time complexity focuses on processing the array of functions.

Space Complexity: O(n)

  • We allocate an array of size n to store the results.
  • Additionally, closures and internal state tracking require some memory, but it’s proportional to the input size.

✅ Example Walkthrough

Input:

const functions = [
    () => new Promise(res => setTimeout(() => res(1), 300)),
    () => new Promise(res => setTimeout(() => res(2), 200)),
    () => new Promise(res => setTimeout(() => res(3), 100))
];
promiseAll(functions).then(console.log); // Expected Output: [1, 2, 3]


Explanation:

  1. The first function resolves after 300ms, the second after 200ms, and the third after 100ms.
  2. The output maintains the original order: [1, 2, 3], even though the promises resolved at different times.

šŸ“Œ Edge Cases to Consider

  • Empty Array: If the input is an empty array, the function should resolve immediately with an empty array ([]).
  • Immediate Rejection: If any promise rejects, the function should immediately reject, stopping further processing.

Example Edge Case:

const functions = [
    () => new Promise((_, rej) => setTimeout(() => rej("Error"), 100)),
    () => new Promise(res => setTimeout(() => res(2), 200)),
    () => new Promise(res => setTimeout(() => res(3), 300))
];
promiseAll(functions).catch(console.error); // Expected Output: "Error"


šŸš€ Conclusion

This exercise demonstrates how Promise.all() works internally and sharpens our understanding of JavaScript’s concurrency model. By implementing it ourselves, we better appreciate how promise handling works behind the scenes.

If you found this solution helpful, check out my LeetCode Solution for more insights.


I hope you enjoyed this breakdown of the Promise.all() implementation! Feel free to share your thoughts or questions in the comments. 😊

Comments

Popular posts from this blog

3Sum Closest: O(n²) Two-Pointer Magic šŸš€ leetcode: 16

Kadane's Algorithm: Maximum Subarray Problem - LeetCode(53) Solution

LeetCode Problem: Implement pow(x, n) leetcode:- 50