In modern web applications, managing asynchronous tasks efficiently is crucial, especially when dealing with tasks that consume resources or depend on external APIs. In this article, we’ll explore how to build a Task Manager in JavaScript that enforces a concurrency limit and queues tasks if the limit is reached. This pattern is essential for creating scalable applications that perform smoothly under load.
Need to build a Concurrency-Controlled Task Manager which:
- Limits the number of tasks running concurrently.
- Queues additional tasks when the concurrency limit is reached.
- Executes queued tasks as soon as a running task completes.
This approach is invaluable in scenarios such as:
- Managing API requests to avoid hitting rate limits.
- Handling resource-intensive operations without overwhelming the system.
- Improving overall application performance by controlling the flow of asynchronous tasks.
Key Focus
- Concurrency Limit: The maximum number of tasks that can run at the same time.
- Queueing: When the concurrency limit is reached, tasks are placed in a queue and executed sequentially as running tasks finish.
Implementation Strategy
We’ll build a TaskManager
class that:
- Accepts tasks (functions that return promises).
- Runs tasks up to the defined concurrency limit.
- Queues additional tasks.
- Automatically starts a queued task once a running task completes.
Below is an example implementation of a concurrency-controlled Task Manager:
class TaskManager {
constructor(concurrencyLimit) {
this.concurrencyLimit = concurrencyLimit;
this.currentRunning = 0;
this.taskQueue = [];
}
/**
* Adds a task to the Task Manager.
* @param {Function} taskFn - A function that returns a promise.
* @returns {Promise} - A promise that resolves or rejects when the task completes.
*/
addTask(taskFn) {
return new Promise((resolve, reject) => {
const task = async () => {
try {
this.currentRunning++;
const result = await taskFn();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.currentRunning--;
// Start next task in queue if available.
if (this.taskQueue.length > 0) {
const nextTask = this.taskQueue.shift();
nextTask();
}
}
};
if (this.currentRunning < this.concurrencyLimit) {
task();
} else {
this.taskQueue.push(task);
}
});
}
}
// Example usage:
// Simulate an asynchronous task
const simulateTask = (id, duration) => {
return () =>
new Promise((resolve) => {
console.log(`Task ${id} started.`);
setTimeout(() => {
console.log(`Task ${id} completed.`);
resolve(`Result of Task ${id}`);
}, duration);
});
};
const manager = new TaskManager(2); // Allow maximum of 2 tasks concurrently
// Adding tasks with different durations
manager.addTask(simulateTask(1, 3000)).then(console.log);
manager.addTask(simulateTask(2, 2000)).then(console.log);
manager.addTask(simulateTask(3, 1000)).then(console.log);
manager.addTask(simulateTask(4, 4000)).then(console.log);
Explanation
- Constructor:
TheTaskManager
constructor initializes:concurrencyLimit
: The maximum number of tasks allowed to run simultaneously.currentRunning
: A counter for currently running tasks.taskQueue
: An array that stores tasks waiting to be executed.
- addTask Method:
- Accepts a task function (
taskFn
) that returns a promise. - Returns a new promise that resolves or rejects based on the task’s outcome.
- Checks if the current number of running tasks is less than the concurrency limit.
- If yes, it runs the task immediately.
- Otherwise, it queues the task.
- After a task completes (whether successfully or not), it decrements the running counter and starts the next task from the queue if available.
- Accepts a task function (
- Simulated Tasks:
- The
simulateTask
function simulates asynchronous tasks by returning a promise that resolves after a given duration. - Multiple tasks are added to the
TaskManager
to demonstrate how tasks are managed concurrently and queued when necessary.
- The
This is also one of the good interview questions, Stay tuned to Rowdy Coders for more in-depth interview articles on JavaScript, system design, and interview preparation. Happy coding!