GitHub
Prototyppackages/task-manager/src

Task Manager

@ralphschuler/task-manager

Ordnet asynchrone Aufgaben einer begrenzten Anzahl Ausführungsslots zu und hält Warteschlange sowie Lebenszyklus zusammen.

queueconcurrencyscheduling

01 · Problem

Wofür braucht man das?

Unbegrenztes paralleles Starten überlastet CPU, Netzwerk oder Fremdsysteme; eine Queue muss Fairness, Resultate, Abbruch und Shutdown definieren.

02 · Denkmodell

Das mentale Modell

Aufgaben warten in einer Queue. Jeder freie Slot nimmt genau eine Aufgabe, gibt ihr Ergebnis zurück und startet danach den nächsten Eintrag.

Im Repository

TaskManager liegt ohne Root-Export vor und hängt von den problematischen Modulen priority-queue und worker-pool ab. Es pollt stapelweise im Sekundentakt, addTask und Interface widersprechen sich, und destroy weist wartende Promises nicht zurück.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Aufgabe mit resolve/reject in die Warteschlange legen.
  2. Solange Slots frei sind, den nächsten Eintrag starten.
  3. Resultat oder Fehler an den ursprünglichen Aufrufer weiterreichen.
  4. Slot im finally freigeben und sofort die nächste Aufgabe pumpen.
  5. Beim Shutdown neue Aufgaben ablehnen und Wartende definiert beenden.

04 · Bauteile

Die entscheidenden Verträge

TaskManager Experimenteller Scheduler auf PriorityQueue und WorkerPool.
addTask Soll eine Aufgabe einreihen und ihr Resultat zurückliefern.
destroy Stoppt Verarbeitung, behandelt wartende Aufgaben derzeit aber unvollständig.

05 · Build it yourself

Selbst implementieren

Ein begrenzter Promise-Scheduler zeigt das Prinzip ohne Polling und ohne echte Worker.

  1. Wartende Starts als Funktionen in einer FIFO-Queue speichern.
  2. Aktive Anzahl vor dem Start erhöhen und in finally verringern.
  3. pump nach enqueue und nach jeder Fertigstellung aufrufen.
minimal.ts · unabhängig vom Package
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 · Verifizieren

Was du testen solltest

  • Nie laufen mehr Aufgaben als das konfigurierte Limit gleichzeitig.
  • Erfolg, Throw und Promise-Rejection geben den Slot in allen Fällen frei.
  • Shutdown entscheidet für jede wartende Aufgabe mit einem dokumentierten Fehler.

07 · Grenzen

Kompromisse und Stolperfallen

  • FIFO ist fair und kennt ohne Zusatzstruktur keine Prioritäten.
  • Promise-Concurrency begrenzt I/O, verschiebt CPU-Arbeit aber nicht in einen anderen Thread.
  • Abbruch benötigt ein Signal, das sowohl Queue als auch laufende Aufgabe verstehen.
Wichtig

WorkerPool und TaskQueue lösen verschiedene Probleme: Parallelität über Threads versus Begrenzung asynchroner Arbeit.

08 · Weiterdenken

Quellcode und Nachbarn

Originalcode auf GitHub ansehen