Modern JavaScript concurrency control library
npm install task-controller
A set of classes that provide assistance with the concurrent access to shared resources and the control of asynchronous tasks.
- Locks
- LockController: a class that manages concurrent access to resources.
- Tasks
- TaskController: a class that manages concurrent asynchronous tasks execution.
- MultiStepController: a class to adjust the concurrency at step level on multi step tasks execution.
``bash`
npm install task-controller
Provides a mechanism to control concurrent access to resources.
#### Constructor
new LockController(options?: LockControllerOptions);
#### Options
- concurrency (number, default: 1) maximum concurrent access to the resource.queueType
- ("FIFO" | "LIFO", default: "FIFO")releaseTimeout
- FIFO: first request, first acquire.
- LIFO: last request, first acquire.
- (milliseconds > 0, defaults: none) prevent a task to acquire the lock indefinitely. If the lock has not already been released by the time the timeout is reached, it is released automatically.releaseTimeoutHandler
- (callback, defaults: none) function to handle releaseTimeout event.
#### API Reference
- acquire(): Promise<() => void>isLocked(): boolean
Acquires a lock and returns a release function. The release function must be called to free the lock.
- getQueueLength(): number
Returns true if the lock is currently acquired by any task.
- clearQueue(): void
Returns the number of tasks waiting to acquire the lock.
-
Removes all pending tasks from the queue without executing them.
#### How to use
`typescript
import { LockController } from "task-controller";
export async function exampleLockControllerWithConcurrency(concurrency: number){
const lockController = new LockController({ concurrency });
const accessTheResource = async (taskId: number) => {
const release = await lockController.acquire();
console.log(Task ${taskId} acquire the lock);release
try {
// access the resource protected by this lock
await setTimeout(1, 'just to simulate some logic');
} finally {
// IMPORTANT: Make sure to always call the function.Task ${taskId} release the lock
release();
console.log();
}
};
await Promise.all([accessTheResource(1), accessTheResource(2), accessTheResource(3)]);
}
`exampleLockControllerWithConcurrency(1);`console`
Task 1 acquire the lock
Task 1 release the lock
Task 2 acquire the lock
Task 2 acquire the lock
Task 3 acquire the lock
Task 3 release the lockexampleLockControllerWithConcurrency(2);`console`
Task 1 acquire the lock
Task 2 acquire the lock
Task 1 release the lock
Task 3 acquire the lock
Task 2 release the lock
Task 3 release the lock
Provides a mechanism to control concurrent asynchronous tasks execution.
#### Constructor
new TaskControllerT
- : the type returned by the task
#### Options
- concurrency (number, default: 1) maximum concurrent task execution.queueType
- ("FIFO" | "LIFO", default: "FIFO")waitingTimeout
- FIFO: first request, first run.
- LIFO: last request, first run.
- (milliseconds > 0, defaults: none) if a task reaches its timeout before being selected for execution, it is automatically discarded.waitingTimeoutHandler
- (callback, defaults: none) function to handle waitingTimeout event.releaseTimeout
- (milliseconds > 0, defaults: none) if a running task exceeds its timeout limit before completing, it will continue running, but will be marked as expired. This enables another task to be selected for execution.releaseTimeoutHandler
- (callback, defaults: none) function to handle releaseTimeout event.errorHandler
- (callback, defaults: none) function to handle task error event.signal
- (AbortSignal, defaults: none) once the signal has been aborted, no more tasks will be selected for execution. Any tasks that are currently running will continue as normal until completion.
#### API Reference
- runForEachrunMany
Executes a task for each entity in the array, respecting the concurrency limit. Returns an array with the results of each task execution.
- run
Executes multiple tasks, respecting the concurrency limit. Returns an array with the results of each task execution.
- isRunning(): boolean
Executes a single task, respecting the concurrency limit. Returns the result of the task execution.
- getQueueLength(): number
Returns true if there are tasks currently running.
- clearQueue(): void
Returns the number of tasks waiting to be executed.
- on(event: string, listener: (...args: any[]) => void): void
Removes all pending tasks from the queue without executing them.
- off(event: string, listener: (...args: any[]) => void): void
Adds an event listener for task events (error, waitingTimeout, releaseTimeout).
-
Removes an event listener for task events.
#### How to use
`typescript
import { TaskController } from "task-controller";
export async function exampleTaskControllerWithConcurrency(concurrency: number){
const taskController = new TaskController({ concurrency });
const task = async (taskId: number) {
console.log(Task ${taskId} selected to be executed);
await setTimeout(1, 'just to simulate some logic');
console.log(Task ${taskId} finished);
}
await taskController.runForEach([ 1, 2, 3 ], task);
}
`exampleTaskControllerWithConcurrency(1);`console`
Task 1 selected to be executed
Task 1 finished
Task 2 selected to be executed
Task 2 finished
Task 3 selected to be executed
Task 3 finishedexampleTaskControllerWithConcurrency(2);`console`
Task 1 selected to be execute
Task 2 selected to be executed
Task 1 finished
Task 3 selected to be executed
Task 2 finished
Task 3 finished$3
Provides a mechanism to control concurrent multi step tasks execution.
#### Constructor
new MultiStepControllerT
- : the type returned by the taskN
- : number of steps
#### Options
- stepConcurrencies (number[], mandatory) the cuncurrency limit of each step.
#### API Reference
- runForEach - Executes a task for each entity with step concurrency controlrunMany
- - Executes multiple tasks with step concurrency controlrun
- - Executes a single task with step concurrency control
#### How to use
`typescript
import { MultiStepController } from "task-controller";
export async function exampleMultiStepControllerWithConcurrency(){
const multiStepController = new MultiStepController
const task = async (
stepLocks: FixedLengthArray
entity: { taskId: number; step1Timeout: number; step2Timeout: number }
) => {
const release1 = await stepLocks[0].acquire();
try {
console.log(Task ${entity.taskId} selected to execute step 1);Task ${entity.taskId} finished step 1
await setTimeout(entity.step1Timeout, "just to simulate some logic");
console.log();
} finally {
release1();
}
const release2 = await stepLocks[1].acquire();
try {
console.log(Task ${entity.taskId} selected to execute step 2);Task ${entity.taskId} finished step 2
await setTimeout(entity.step2Timeout, "just to simulate some logic");
console.log();
} finally {
release2();
}
};
await multiStepController.runForEach(
[
{ taskId: 1, step1Timeout: 40, step2Timeout: 120 },
{ taskId: 2, step1Timeout: 30, step2Timeout: 50 },
{ taskId: 3, step1Timeout: 30, step2Timeout: 50 },
],
task
);
}
`exampleMultiStepControllerWithConcurrency();`console``
Task 1 selected to execute step 1
Task 1 finished step 1
Task 2 selected to execute step 1
Task 1 selected to execute step 2
Task 2 finished step 1
Task 3 selected to execute step 1
Task 2 selected to execute step 2
Task 3 finished step 1
Task 2 finished step 2
Task 3 selected to execute step 2
Task 1 finished step 2
Task 3 finished step 2