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.
Plant priorisierte asynchrone Tasks innerhalb einer festen Parallelitätsgrenze. Shutdown weist wartende Arbeit zurück und lässt aktive Tasks enden. CPU-Isolation und persistente Queues sind separate Aufgaben.
03 · Kontrollfluss
Was passiert in welcher Reihenfolge?
- Aufgabe mit resolve/reject in die Warteschlange legen.
- Solange Slots frei sind, den nächsten Eintrag starten.
- Resultat oder Fehler an den ursprünglichen Aufrufer weiterreichen.
- Slot im finally freigeben und sofort die nächste Aufgabe pumpen.
- Beim Shutdown neue Aufgaben ablehnen und Wartende definiert beenden.
04 · Bauteile
Die entscheidenden Verträge
TaskManager.addTaskLiefert ein Promise für einen priorisierten Task.destroyLehnt wartende Arbeit ab und stoppt neue Tasks.05 · Selbst bauen
Selbst implementieren
Ein begrenzter Promise-Scheduler zeigt das Prinzip ohne Polling und ohne echte Worker.
- Wartende Starts als Funktionen in einer FIFO-Queue speichern.
- Aktive Anzahl vor dem Start erhöhen und in finally verringern.
- pump nach enqueue und nach jeder Fertigstellung aufrufen.
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 solltest du testen?
- 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.
Selbst ausprobieren
Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.
Nie laufen mehr Aufgaben als das konfigurierte Limit gleichzeitig.
Ansatz vergleichen
Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.
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.
Shutdown weist wartende Arbeit zurück und lässt aktive Tasks enden. CPU-Isolation und persistente Queues sind separate Aufgaben.
08 · Weiterdenken
Quellcode und verwandte Ideen
yarn tsx packages/task-manager/examples/basic.ts