Aufgabenwarteschlangen in TypeScript: Ein praktischer Leitfaden

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Arbeiten mit Aufgabenwarteschlangen in TypeScript: In der Praxis

\n

In modernen Webanwendungen besteht häufig die Notwendigkeit, langwierige oder ressourcenintensive Operationen asynchron auszuführen, ohne den Hauptthread des Benutzers zu blockieren. Das Versenden von E-Mail-Newslettern, das Generieren von PDF-Berichten, das Verarbeiten hochgeladener Bilder – all das sind ideale Kandidaten für die Auslagerung in Hintergrundaufgaben. Eine Aufgabenwarteschlange (task queue) ist ein bewährtes Architekturmuster, das eine zuverlässige Ausführung solcher Operationen ermöglicht. In diesem Artikel werden wir uns ansehen, wie man Aufgabenwarteschlangen in TypeScript praktisch erstellt und nutzt, unter Verwendung gängiger Bibliotheken und Ansätze.

\n

Wir werden die Erstellung einer einfachen Warteschlange, die Arbeit mit Prioritäten, die Fehlerbehandlung sowie die Integration mit Redis über die Bibliothek BullMQ behandeln. Alle Beispiele werden in TypeScript mit Fokus auf strenge Typisierung geschrieben.

\n\n

1. Grundlagen: Erstellen einer einfachen Aufgabenwarteschlange

\n

Zunächst implementieren wir eine einfache Aufgabenwarteschlange ohne externe Abhängigkeiten. Dies hilft, die grundlegenden Funktionsprinzipien zu verstehen. Unsere Warteschlange speichert Aufgaben-Funktionen und führt sie nacheinander aus.

\n
type Task<T> = () => Promise<T>;\n\ninterface TaskItem<T> {\n  task: Task<T>;\n  resolve: (value: T | PromiseLike<T>) => void;\n  reject: (reason?: any) => void;\n}\n\nclass SimpleQueue {\n  private queue: TaskItem<any>[] = [];\n  private isProcessing = false;\n\n  add<T>(task: Task<T>): Promise<T> {\n    return new Promise<T>((resolve, reject) => {\n      this.queue.push({ task, resolve, reject });\n      this.process();\n    });\n  }\n\n  private async process(): Promise<void> {\n    if (this.isProcessing || this.queue.length === 0) return;\n    this.isProcessing = true;\n\n    const item = this.queue.shift()!;\n    try {\n      const result = await item.task();\n      item.resolve(result);\n    } catch (error) {\n      item.reject(error);\n    } finally {\n      this.isProcessing = false;\n      this.process();\n    }\n  }\n}\n\n// Verwendung\nconst queue = new SimpleQueue();\n\nqueue.add(async () => {\n  console.log('Aufgabe 1 gestartet');\n  await new Promise(r => setTimeout(r, 1000));\n  console.log('Aufgabe 1 abgeschlossen');\n  return 'Ergebnis 1';\n});\n\nqueue.add(async () => {\n  console.log('Aufgabe 2 gestartet');\n  return 'Ergebnis 2';\n});
\n

Dieses Beispiel zeigt die grundlegende Mechanik: Aufgaben werden zu einem Array hinzugefügt und der Reihe nach ausgeführt. Jede Aufgabe gibt ein Promise zurück, was die Arbeit mit den Ergebnissen erleichtert. Allerdings hat dieser Ansatz Einschränkungen: Es gibt keine Unterstützung für Prioritäten, Wiederholungsversuche und das Speichern des Zustands beim Neustart der Anwendung.

\n\n

2. Priorisierung und Fehlerbehandlung

\n

In realen Projekten haben Aufgaben oft unterschiedliche Wichtigkeit. Beispielsweise sollte das Senden einer transaktionalen E-Mail vor der Bereinigung temporärer Dateien ausgeführt werden. Fügen wir unserer Warteschlange Prioritäten hinzu.

\n
type Priority = 'high' | 'medium' | 'low';\n\ninterface PriorityTask<T> {\n  task: Task<T>;\n  priority: Priority;\n  resolve: (value: T) => void;\n  reject: (reason?: any) => void;\n  retries: number;\n}\n\nclass PriorityQueue {\n  private high: PriorityTask<any>[] = [];\n  private medium: PriorityTask<any>[] = [];\n  private low: PriorityTask<any>[] = [];\n  private isProcessing = false;\n  private maxRetries = 3;\n\n  add<T>(task: Task<T>, priority: Priority = 'medium'): Promise<T> {\n    return new Promise<T>((resolve, reject) => {\n      const taskItem: PriorityTask<T> = {\n        task,\n        priority,\n        resolve,\n        reject,\n        retries: 0\n      };\n      this.getQueue(priority).push(taskItem);\n      this.process();\n    });\n  }\n\n  private getQueue(priority: Priority): PriorityTask<any>[] {\n    switch (priority) {\n      case 'high': return this.high;\n      case 'medium': return this.medium;\n                

Blogs

Buchempfehlungen