slot-pool
v1.0.3
Published
A lightweight, zero-dependency promise pool for controlling concurrent asynchronous tasks with automatic queuing.
Maintainers
Readme
Slot Pool
A lightweight, zero-dependency TypeScript concurrency pool for limiting the number of tasks running at the same time.
Supports both synchronous and asynchronous tasks. When a task finishes, the next queued task automatically starts.
Features
- Lightweight
- Zero dependencies
- TypeScript support
- Limit concurrent tasks
- Supports synchronous and asynchronous functions
- Automatic task queue
- Return task results with
Promise<T> - Optional callback for task results
- Automatically releases slots when tasks throw errors
- Generic result type inference
- Queue status helpers (waiting(), isEmpty()
Basic Usage
import { SlotPool } from "slot-pool";
const pool = new SlotPool(2);
pool.run(() => {
console.log("Task 1");
});
pool.run(async () => {
console.log("Task 2 START");
await new Promise<void>((resolve) => {
setTimeout(resolve, 2000);
});
console.log("Task 2 END");
});The number 2 means that a maximum of 2 tasks can run concurrently.
Both synchronous and asynchronous functions are supported.
Synchronous Tasks
You can pass a normal synchronous function:
const pool = new SlotPool(2);
const result = await pool.run(() => {
return {
id: 1,
message: "Task completed",
};
});
console.log(result);Even though the task itself is synchronous, run() always returns a Promise<T>.
Asynchronous Tasks
You can also pass an asynchronous function:
const pool = new SlotPool(2);
const result = await pool.run(async () => {
await new Promise<void>((resolve) => {
setTimeout(resolve, 1000);
});
return {
id: 1,
message: "Task completed",
};
});
console.log(result);Mixed Tasks
Synchronous and asynchronous tasks can be used together:
const pool = new SlotPool(2);
pool.run(() => {
console.log("Sync task");
});
pool.run(async () => {
console.log("Async task START");
await new Promise<void>((resolve) => {
setTimeout(resolve, 2000);
});
console.log("Async task END");
});
pool.run(() => {
console.log("Another sync task");
});The pool treats both types as tasks and limits the number of currently executing tasks according to the pool size.
API
new SlotPool(size)
Creates a new Slot Pool.
const pool = new SlotPool(10);Parameters
| Parameter | Type | Description |
| --------- | -------- | ------------------------------- |
| size | number | Maximum number of running tasks |
The size must be a positive integer.
pool.run(task, callback?)
Runs a synchronous or asynchronous task through the Slot Pool.
pool.run(task, callback?);task
() => T | Promise<T>;The function that should be executed.
The function can either:
- return a value synchronously
- return a
Promise<T>asynchronously
Examples:
pool.run(() => {
return 123;
});pool.run(async () => {
return 123;
});callback
(data: T) => voidOptional callback that receives the result returned by the task.
The callback is called after the task successfully completes.
Return Value
Promise<T>;run() always returns a promise containing the task result.
For a synchronous task:
const result = await pool.run(() => {
return 123;
});
console.log(result);For an asynchronous task:
const result = await pool.run(async () => {
return 123;
});
console.log(result);Both produce:
123TypeScript Types
The main method is typed as:
async run<T>(
task: () => T | Promise<T>,
callback?: (data: T) => void,
): Promise<T>This allows TypeScript to automatically infer the result type for both synchronous and asynchronous functions.
For example:
const result = await pool.run(() => {
return {
id: 123,
name: "John",
};
});TypeScript automatically knows:
result.id;
result.name;The same works with asynchronous functions:
const result = await pool.run(async () => {
return {
id: 123,
name: "John",
};
});Error Handling
If a task throws an error, the error is propagated normally.
const pool = new SlotPool(2);
try {
await pool.run(() => {
throw new Error("Something went wrong");
});
} catch (error) {
console.error(error);
}The pool automatically releases the slot even when the task fails.
This ensures that a failed task does not permanently occupy a slot.
Because await also handles normal values, this works for both:
() => T;and:
() => Promise<T>;pool.waiting()
Returns the number of tasks currently waiting in the queue.
const count = pool.waiting();
console.log(`${count} tasks waiting in queue`);pool.isEmpty()
Checks whether the pool is completely idle (no actively executing tasks and no tasks waiting in the queue).
if (pool.isEmpty()) {
console.log("All tasks are completed");
}Important
SlotPool does not create Node.js Worker Threads.
It is a concurrency controller for JavaScript tasks.
For example:
const pool = new SlotPool(4);means:
Maximum 4 tasks executing at onceIt does not mean:
4 Node.js Worker ThreadsComplete API Overview Table
If you maintain a summary table in your README, here is how the complete API interface looks:
| Method / Property | Return Type | Description |
| :--------------------- | :----------- | :-------------------------------------------------------- |
| new SlotPool(size) | SlotPool | Instantiates a pool limiting concurrency to size. |
| run(task, callback?) | Promise<T> | Schedules a task to run when a slot is free. |
| waiting() | number | Returns the count of tasks waiting in the queue. |
| isEmpty() | boolean | Returns true if active === 0 and no tasks are queued. |
SlotPool is useful for:
- HTTP requests
- API calls
- Database operations
- File operations
- Network operations
- Web scraping
- Synchronous operations
- Asynchronous operations
- Any workload that needs a concurrency limit
