PoolTaskExecutor
PoolTaskExecutor executes submitted task functions with a fixed concurrency limit. Use it when different call sites should share the same pool capacity instead of creating independent queues or Promise.all() bursts.
Best for shared concurrency limits Use
PoolTaskExecutorfor API clients, background processors, or service adapters that need one shared concurrency budget across many callers.
Import
Section titled “Import”Root package:
import { PoolTaskExecutor } from "@vgerbot/async";Module subpath:
import { PoolTaskExecutor } from "@vgerbot/async/executors";Leaf subpath:
import { PoolTaskExecutor } from "@vgerbot/async/executors/PoolTaskExecutor";Quick example
Section titled “Quick example”import { PoolTaskExecutor } from "@vgerbot/async";
const pool = new PoolTaskExecutor(2);
const user = pool.exec(async (token) => { const response = await token.wrap(fetch("/api/users/1")); return response.json();}, { kind: "user", name: "load-user" });
const posts = pool.exec(async (token) => { const response = await token.wrap(fetch("/api/users/1/posts")); return response.json();}, { kind: "posts", name: "load-posts" });
const result = await Promise.all([user, posts]);console.log(result);When to use PoolTaskExecutor
Section titled “When to use PoolTaskExecutor”- Shared pool: Multiple modules submit work to the same concurrency limit.
- Task functions: Callers submit arbitrary async tasks instead of task payloads for a fixed worker.
- Cancellation groups: Pending tasks can be labelled with
kindfor selective cancellation. - Executor abstraction: Code can depend on
ITaskExecutorand swap scheduling policies later.
class PoolTaskExecutor extends BaseTaskExecutor { constructor(concurrency: number); exec<T>(task: AsyncTask<T>, options?: TaskOptions): TaskHandle<T>; cancel(reason?: unknown): void; cancel(options: TaskCancelOptions): void; cancel(reason: unknown, options: TaskCancelOptions): void; isCancelled(): boolean; shutdown(reason?: unknown): void;}Constructor
Section titled “Constructor”new PoolTaskExecutor(concurrency: number)| Parameter | Type | Description |
|---|---|---|
concurrency |
number |
Number of worker loops created for the executor. |
exec(task, options)
Section titled “exec(task, options)”Submits a task to the pool and returns a task handle for that task result.
type AsyncTask<T> = (token: CancellableToken) => Promise<T>;| Parameter | Type | Description |
|---|---|---|
task |
AsyncTask<T> |
Async function that receives a CancellableToken. |
options |
TaskOptions |
Optional task labels and metadata. |
TaskOptions
Section titled “TaskOptions”| Option | Type | Description |
|---|---|---|
kind |
string |
Group key for selective cancellation of matching pending tasks. |
name |
string |
Human-readable task name. |
metadata |
Record<string, unknown> |
Caller-defined metadata. |
Return value
Section titled “Return value”exec() returns TaskHandle<T>. It is awaitable and also provides cancel() for per-task cancellation.
Execution model
Section titled “Execution model”PoolTaskExecutor creates worker loops when constructed.
exec()checks permanent executor shutdown.- The task is queued with its resolved task options.
- The next available worker dequeues the task.
- The worker calls the task with a
CancellableToken. - The
exec()promise resolves or rejects with the task outcome. - The worker continues to the next queued task until the executor is shut down.
const pool = new PoolTaskExecutor(1);
const first = pool.exec(async () => "first");const second = pool.exec(async () => "second");
console.log(await first);console.log(await second);Error handling
Section titled “Error handling”Task errors reject only that task’s exec() promise. A non-cancellation task error does not permanently cancel the pool.
const pool = new PoolTaskExecutor(2);
const failed = pool.exec(async () => { throw new Error("Request failed");});
await failed.catch((error) => { console.error(error.message);});
const ok = await pool.exec(async () => "still running");console.log(ok);Cancellation
Section titled “Cancellation”Calling cancel(reason) cancels matching tasks but does not permanently disable the executor.
const pool = new PoolTaskExecutor(2);
const slow = pool.exec(async (token) => { await token.sleep(5_000); return "done";});
pool.cancel("App shutdown");await slow;Use shutdown(reason) to permanently close the executor. After shutdown, exec() throws ExecutorShutdownError.
Selective cancellation removes matching pending tasks by kind without permanently disabling the executor when matches are found.
const pool = new PoolTaskExecutor(1);
pool.exec(loadUser, { kind: "user" });pool.exec(loadPosts, { kind: "posts" });
pool.cancel({ kind: "posts", reason: "Posts refreshed" });If no task matches a selective cancellation request, the executor remains usable.
TypeScript tips
Section titled “TypeScript tips”Use ITaskExecutor when code should not depend on a specific scheduling policy.
import type { ITaskExecutor } from "@vgerbot/async";
async function loadWithExecutor(executor: ITaskExecutor) { return executor.exec(async (token) => { await token.sleep(100); return "loaded"; });}Related APIs
Section titled “Related APIs”queueuses a fixed worker function and exposes lifecycle hooks.- Executors lists all executor implementations.
- Concurrency Patterns compares executor pools with queues and control-flow helpers.