Concurrency Limited Task Scheduler
You are given an array of asynchronous functions called tasks, where each function returns a Promise resolving to a value. Your goal is to implement a function that executes these tasks with a concurrency limit that is, no more than maxConcurrent tasks can be running at the same time.
Once a task finishes, the scheduler should immediately start the next task (if any remain). The order of returned results should match the order of the input tasks, regardless of the order in which they resolve.
This is a classic problem often seen in API batching, file uploads, and load-balanced parallel computing.
Input:
tasks: An array of functions() => Promise<T>— each function returns a promise that resolves to any primitive value (e.g., number, string).maxConcurrent: A positive integer indicating the maximum number of tasks that can run in parallel at any given time.
Output:
- A
Promisethat resolves to an array of values, in the same order as the input tasks array. - Each element in the result corresponds to the resolved value of its respective task.
Constraints & Edge Cases:
1 <= tasks.length <= 10001 <= maxConcurrent <= tasks.length- Each task function:
- Returns a promise that resolves successfully (you may optionally handle errors, but it's not required unless specified).
- May resolve at different speeds (simulate delay with
setTimeout). - Tasks should start execution as soon as a slot is available (i.e., don’t batch unnecessarily).
- The resolved result must maintain the input order regardless of the order in which tasks complete.
- Ensure no more than
maxConcurrenttasks are actively running at any time. - Support cases where
maxConcurrent === 1(fully sequential). - Should work correctly even if all promises resolve immediately or have
0msdelay. - Bonus: Prevent stack overflow or performance issues when
tasks.lengthis large (optional for extra credit).
Example Inputs & Outputs
// Example 1: maxConcurrent = 2, each task resolves after a delay const tasks = [ () => delayTask(100, "A"), () => delayTask(50, "B"), () => delayTask(10, "C"), ]; const maxConcurrent = 2; Output: ["A", "B", "C"] // Explanation: Two tasks can run at once. "B" and "C" may finish before "A", // but results are returned in the same order as the task list. // Example 2: maxConcurrent = 1 (sequential execution) const tasks = [ () => delayTask(10, 1), () => delayTask(20, 2), () => delayTask(30, 3), ]; const maxConcurrent = 1; Output: [1, 2, 3] // Explanation: Only one task runs at a time, so tasks run in order and complete sequentially. // Example 3: All tasks resolve immediately const tasks = [ () => Promise.resolve("X"), () => Promise.resolve("Y"), () => Promise.resolve("Z"), ]; const maxConcurrent = 3; Output: ["X", "Y", "Z"] // Explanation: All tasks complete instantly. No concurrency constraints are hit. // Example 4: maxConcurrent > tasks.length const tasks = [ () => delayTask(5, "first"), () => delayTask(5, "second"), ]; const maxConcurrent = 5; Output: ["first", "second"] // Explanation: All tasks can run at the same time since concurrency limit is higher than task count. // Helper used in test examples function delayTask(ms, result) { return () => new Promise(resolve => setTimeout(() => resolve(result), ms)); }
Companies:
Solve Similar questions 🔥
Want to upskill? Explore our courses!
Namaste DSA
Master DSA from scratch with numerous problems, and expert guidance.
Namaste React
Wanna dive deep into React and become Frontend Expert? Learn with me now!
Namaste Frontend System Design
The most comprehensive and detailed course for frontend system design.
Namaste Node.js
Wanna dive deep into Node.js? Enroll into `Namaste Node.js` now!
