Talentd
FleetCode
Make and follow sheets.Sign in

Execute Asynchronous Functions in Parallel solution and explanation

Medium7 min read76.0% acceptanceUpdated 9 Apr 2026
Execute Asynchronous Functions in Parallel | Leetcode 2721 | 30 Days of JavaScript #javascriptLearn With Chirag · 8:57 · 2K viewsWatch on YouTube
9 more videos on this problem
  1. Execute Asynchronous Functions in Parallel | Leetcode 2721 | 30 Days of JavaScript #javascriptLearn With Chirag · 8:57 · 2K views
  2. 2721. Execute Asynchronous Functions in Parallel - Leetcode JavaScript Solution with ExplanationEndeavour Monk · 16:42 · 1K views
  3. #day19 | 2721. Execute Asynchronous Functions in Parallel | 30 Days of JavaScript | Leetcode-2721Coding with Ankita · 22:21 · 714 views
  4. 2721. Execute Async Funcs in Parallel - Leetcode (Medium). 30 Days of JavaScript | День 19 | .all()dev tsx · 3:31 · 254 views
  5. LeetCode 30 Days of JavaScript: Execute Asynchronous Functions in ParallelWeb Wizard · 6:30 · 126 views
  6. Execute Asynchronous Functions in Parallel - 2721 Leetcode Problem - 30 days of JavaScriptanalyse_code_test · 5:37 · 110 views
  7. #2721 Execute Asynchronous Functions in Parallel #leetcode | 30 Days of JavaScript #javascriptdevProgrammers choice · 9:01 · 26 views
  8. JavaScript || 30 days JavaScript challenge || execute-asynchronous-functions-in-parallel || 2721the JS guy · 15:17 · 24 views
  9. 2721 Execute Asynchronous Functions in Parallelmohamed antar · 5:35 · 21 views
  10. 2721. Run Async Functions in Parallel | JavaScript PromiseAll CloneCodeCrafting with Vishnu · 6:57 · 20 views

The problem

Given an array of asynchronous functions functions, return a new promise promise. Each function in the array accepts no arguments and returns a promise. All the promises should be executed in parallel.

promise resolves:

  • When all the promises returned from functions were resolved successfully in parallel. The resolved value of promise should be an array of all the resolved values of promises in the same order as they were in the functions. The promise should resolve when all the asynchronous functions in the array have completed execution in parallel.

promise rejects:

  • When any of the promises returned from functions were rejected. promise should also reject with the reason of the first rejection.

Please solve it without using the built-in Promise.all function.

Example 1:

Input: functions = [
  () => new Promise(resolve => setTimeout(() => resolve(5), 200))
]
Output: {"t": 200, "resolved": [5]}
Explanation: 
promiseAll(functions).then(console.log); // [5]

The single function was resolved at 200ms with a value of 5.

Example 2:

Input: functions = [
    () => new Promise(resolve => setTimeout(() => resolve(1), 200)), 
    () => new Promise((resolve, reject) => setTimeout(() => reject("Error"), 100))
]
Output: {"t": 100, "rejected": "Error"}
Explanation: Since one of the promises rejected, the returned promise also rejected with the same error at the same time.

Example 3:

Input: functions = [
    () => new Promise(resolve => setTimeout(() => resolve(4), 50)), 
    () => new Promise(resolve => setTimeout(() => resolve(10), 150)), 
    () => new Promise(resolve => setTimeout(() => resolve(16), 100))
]
Output: {"t": 150, "resolved": [4, 10, 16]}
Explanation: All the promises resolved with a value. The returned promise resolved when the last promise resolved.

Constraints:

  • functions is an array of functions that returns promises
  • 1 <= functions.length <= 10

Approach overview

Problem Overview: You receive an array of asynchronous functions. Each function returns a promise. Execute all functions in parallel and return a single promise that resolves with their results in the same order. If any promise rejects, the returned promise must reject immediately.

Approach 1: Using Promise Chaining for Resolution (O(n) time, O(n) space)

Create a promise for each async function by calling it immediately so all tasks start in parallel. Use .then() chaining to capture each result and place it at the correct index in a result array. Maintain ordering by storing results based on the original function index rather than completion order. Once all promises resolve, resolve the outer promise with the results array. This mirrors how Promise.all aggregates results internally.

Approach 2: Using Individual Promise Resolution (O(n) time, O(n) space)

Iterate through the array and execute every function immediately so all promises start concurrently. Attach a .then() handler to each promise and store the resolved value in a results array. Track how many promises have completed. When the completion count reaches the number of functions, resolve the main promise with the results array. This approach keeps execution parallel while ensuring ordered output.

Approach 3: Manual Implementation of Promise Handling (O(n) time, O(n) space)

Construct a new promise that internally manages all async calls. For each function, call it and attach both resolve and reject handlers. If any promise rejects, immediately reject the outer promise. Otherwise store the resolved value and continue tracking completions. This approach demonstrates how utilities like Promise.all can be implemented manually using core asynchronous programming and JavaScript promises behavior.

Approach 4: Using a Counter for Resolved Promises (O(n) time, O(n) space)

Initialize a results array and a counter representing how many promises have resolved. Execute each function immediately to ensure parallel execution. When a promise resolves, store the value at its index and increment the counter. Once the counter equals the number of functions, resolve the main promise with the results. This pattern is common in concurrency control when coordinating multiple asynchronous tasks.

Recommended for interviews: The counter-based approach or manual promise handling is usually expected. It proves you understand how parallel asynchronous execution works without relying on built-in helpers like Promise.all. Showing the manual aggregation logic demonstrates strong understanding of promises, ordering guarantees, and failure handling.

Approach 1: Using Promise Chaining for Resolution

This approach involves managing a counter to track resolved promises and ensuring we handle rejection immediately. We loop through each of the asynchronous functions, initiate their execution, and attach then and catch handlers. Using shared variables, we manage the resolved values and first rejection detection.

function promiseAll(functions) {
    return new Promise((resolve, reject) => {
        let results = [];
        let completed = 0;
        functions.forEach((fn, index) => {
            fn().then(value => {
                results[index] = value;
                completed++;
                if (completed === functions.length) {
                    resolve(results);
                }
            }).catch(err => {
                reject(err);
            });
        });
    });
}

This code creates a promise that resolves when all asynchronous functions have resolved, or rejects immediately if any promise is rejected. We maintain a results array to store resolved values and a completed counter to know when all functions have resolved. Each function is called, and on success, we store its result and increment the completed counter. Upon completion of all functions, we resolve the promise with the results.

Complexity
  • Time Complexity: O(n) where n is the number of functions.
  • Space Complexity: O(n) due to the storage of results.
Try this approach in the editor

Approach 2: Using Individual Promise Resolution

This approach focuses on using individual promise resolution for each function and using a shared state to track the first rejection or when all have completed.

function promiseAll(functions) {
    const results = new Array(functions.length);
    let counter = 0;

    return new Promise((resolve, reject) => {
        functions.forEach((func, index) => {
            func().then(value => {
                results[index] = value;
                counter++;
                if (counter === functions.length) {
                    resolve(results);
                }
            }).catch(err => reject(err));
        });
    });
}

This solution initiates each asynchronous function, attaches promise handlers to manage results, and uses a counter to track the number of resolved promises. On the resolution of any promise, we store its result and increase the counter. If all are resolved, we resolve our overall promise with the results. Any rejection leads to an immediate promise rejection.

Complexity
  • Time Complexity: O(n) due to iterating over functions array.
  • Space Complexity: O(n) for storing results.
Try this approach in the editor

Approach 3: Manual Implementation of Promise Handling

This approach manually handles the resolution and rejection of each promise returned by the functions. We'll iterate over the functions list, execute each one in parallel, and use counters or markers to determine when all promises have settled. Upon completion, their results will be consolidated into a single array, keeping track of the order.

function promiseAll(functions) {
  return new Promise((resolve, reject) => {
    const results = [];
    let completed = 0;
    functions.forEach((func, index) => {
      func().then(result => {
        results[index] = result;
        completed++;
        if (completed === functions.length) {
          resolve(results);
        }
      }).catch(reject);
    });
  });
}
We create a new promise. Then, for each function in the array, we execute it and attach 'then' and 'catch' handlers. The 'then' handler updates the results array at the specific index to maintain order. When all promises are resolved, we call 'resolve' with the results array. If any promise is rejected, the entire promise chain is rejected immediately with the first encountered error.
Complexity

Time Complexity: O(n) for n functions and promises.
Space Complexity: O(n) storing the resolved values.

Try this approach in the editor

Approach 4: Using a Counter for Resolved Promises

Here, a counter is used to ensure all promises have resolved before resolving the overall promise. This approach maintains state using closure variables to track the number of resolved promises and collects results into predefined storage (array).

function promiseAll(functions) {
  return new Promise((resolve, reject) => {
    const results = Array(functions.length);
    let resolvedCount = 0;
    functions.forEach((func, index) => {
      func().then(result => {
        results[index] = result;
        resolvedCount++;
        if (resolvedCount === functions.length) {
          resolve(results);
        }
      }).catch(reject);
    });
  });
}
This solution utilizes a counter to keep track of resolved promises. The 'results' array stores each resolved value at a specific index. Once all promises have resolved (i.e., resolvedCount equals the length of the functions array), the main promise is resolved with the results array. If any promise rejects, the catch block causes immediate rejection of the main promise.
Complexity

Time Complexity: O(n) because each promise executes independently.
Space Complexity: O(n) due to the results array storing each resolved value.

Try this approach in the editor

Approach 5: Default Approach

async function promiseAll<T>(functions: (() => Promise<T>)[]): Promise<T[]> {
    return new Promise<T[]>((resolve, reject) => {
        let cnt = 0;
        const ans = new Array(functions.length);
        for (let i = 0; i < functions.length; ++i) {
            const f = functions[i];
            f()
                .then(res => {
                    ans[i] = res;
                    cnt++;
                    if (cnt === functions.length) {
                        resolve(ans);
                    }
                })
                .catch(err => {
                    reject(err);
                });
        }
    });
}

/**
 * const promise = promiseAll([() => new Promise(res => res(42))])
 * promise.then(console.log); // [42]
 */
Try this approach in the editor

Complexity comparison

ApproachComplexity
Using Promise Chaining for Resolution
  • Time Complexity: O(n) where n is the number of functions.
  • Space Complexity: O(n) due to the storage of results.
Using Individual Promise Resolution
  • Time Complexity: O(n) due to iterating over functions array.
  • Space Complexity: O(n) for storing results.
Manual Implementation of Promise Handling

Time Complexity: O(n) for n functions and promises.
Space Complexity: O(n) storing the resolved values.

Using a Counter for Resolved Promises

Time Complexity: O(n) because each promise executes independently.
Space Complexity: O(n) due to the results array storing each resolved value.

Default Approach–

Complexity analysis

ApproachTimeSpaceWhen to Use
Promise Chaining for ResolutionO(n)O(n)When you want a clean promise-based aggregation pattern similar to Promise.all
Individual Promise ResolutionO(n)O(n)When managing each promise independently while preserving order
Manual Promise HandlingO(n)O(n)When implementing custom behavior similar to Promise.all for interviews
Counter for Resolved PromisesO(n)O(n)Most common interview pattern for coordinating parallel async tasks

Frequently asked questions

Is Execute Asynchronous Functions in Parallel easy or hard?

The problem is considered Medium difficulty because it requires understanding asynchronous control flow rather than complex algorithms. The challenge comes from coordinating multiple promises, preserving result order, and handling rejection correctly while keeping execution parallel.

Execute Asynchronous Functions in Parallel Python/Java solution

The original problem targets JavaScript because it focuses on Promise behavior. In other languages the equivalent pattern uses futures or async tasks, such as Python's asyncio.gather or Java's CompletableFuture.allOf. These abstractions also run asynchronous tasks in parallel and aggregate results.

How to solve Execute Asynchronous Functions in Parallel in O(n)?

Call each async function immediately so all promises start concurrently. Attach a resolution handler that stores the result in an array at the correct index and increments a completion counter. Once the counter equals the total number of functions, resolve the outer promise with the results. This processes each promise once, giving O(n) time complexity.

What is the best approach for Execute Asynchronous Functions in Parallel?

The most practical approach uses a counter to track resolved promises while executing all functions immediately. Each async function runs in parallel, its result is stored at the correct index, and the counter increments on completion. When the counter equals the number of functions, the outer promise resolves with the ordered results. Time complexity is O(n) with O(n) space.

Is Execute Asynchronous Functions in Parallel asked at Google/Amazon/Meta?

Problems involving promise coordination, async execution, and concurrency control frequently appear in JavaScript-focused interviews at companies building large web platforms. Interviewers use them to test understanding of promises, event loops, and parallel execution patterns.

What data structure is used in Execute Asynchronous Functions in Parallel?

The core structure is an array used to store resolved results at their original indices. A counter variable or similar state tracker monitors how many promises have completed. The logic relies on JavaScript Promise objects to manage asynchronous execution.

What is the time complexity of Execute Asynchronous Functions in Parallel?

The algorithm runs in O(n) time where n is the number of asynchronous functions. Each function is invoked exactly once and its result is processed once. Additional space of O(n) is required to store the output array and track completion state.

Comments

Now write it yourselfClose this page and solve Execute Asynchronous Functions in Parallel in the editor, without looking.
Practise Execute Asynchronous Functions in Parallel