DeutschGitHub
partialpackages/task-manager/src

Task Manager

@ralphschuler/task-manager

Schedules prioritized asynchronous tasks within a fixed concurrency limit.

queueconcurrencyscheduling

01 · Problem

What is it for?

Starting every promise at once can exhaust resources, while polling wastes time and hides ordering.

02 · Mental model

How should you think about it?

Waiting tasks belong to a priority queue; active tasks occupy a fixed number of slots.

In this repository

Schedules prioritized asynchronous tasks within a fixed concurrency limit. Shutdown rejects waiting work and lets active tasks finish. CPU isolation and persistent queues are separate concerns.

03 · Control flow

What happens in which order?

  1. Validate configured concurrency.
  2. Queue a task and its promise handlers.
  3. Select the highest-priority waiting task.
  4. Start it when a slot is free.
  5. Settle its promise and pump the next task.

04 · Building blocks

The important contracts

TaskManager.addTaskReturn a promise for a prioritized task.
destroyReject waiting work and stop accepting tasks.

05 · Build it yourself

Implement a small core

Start with this small independent implementation, then use the steps below to make its contract explicit.

  1. Validate configured concurrency.
  2. Queue a task and its promise handlers.
  3. Select the highest-priority waiting task.
  4. Start it when a slot is free.
  5. Settle its promise and pump the next task.
Concept sketch · may reference omitted helpers
class TaskQueue {
  private active = 0;
  private waiting: Array<() => void> = [];
  constructor(private limit: number) {}

  run<T>(task: () => Promise<T>) {
    return new Promise<T>((resolve, reject) => {
      this.waiting.push(() => {
        this.active++;
        task().then(resolve, reject).finally(() => { this.active--; this.pump(); });
      });
      this.pump();
    });
  }
  private pump() {
    while (this.active < this.limit && this.waiting.length) this.waiting.shift()?.();
  }
}

06 · Verify

What should you test?

  • Never exceed the concurrency limit.
  • Run higher-priority waiting work first.
  • Reject queued work on shutdown and preserve active results.

Try it yourself

Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.

Never exceed the concurrency limit.

Compare your approach

Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.

07 · Limits

Trade-offs and failure modes

  • This scheduler does not move CPU work off the event loop.
  • Starvation requires a deliberate priority policy.
  • Queue capacity and cancellation need explicit limits.
Important

Shutdown rejects waiting work and lets active tasks finish. CPU isolation and persistent queues are separate concerns.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/task-manager/examples/basic.ts