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.
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?
- 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
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.
- 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 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.
WorkerPool und TaskQueue lösen verschiedene Probleme: Parallelität über Threads versus Begrenzung asynchroner Arbeit.
08 · Weiterdenken