Parallelität in TypeScript: Threads, Goroutinen und async/await – praktisch erklärt

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

Parallelität in TypeScript: Vom Event Loop zu Web Workers

\n\n

Haben Sie schon einmal bemerkt, wie Ihr Browser oder Ihre Node.js-Anwendung während der Ausführung einer komplexen mathematischen Operation oder der Verarbeitung eines großen JSON „einfriert“? Ein vertrautes Gefühl der Ohnmacht, wenn die Oberfläche nicht mehr auf Klicks reagiert und der Server nicht mehr auf neue Anfragen. Der Grund dafür ist, dass JavaScript (und sein strenger Bruder TypeScript) standardmäßig eine Single-Thread-Sprache ist. Aber das bedeutet nicht, dass wir zu langsamen Programmen verdammt sind. Im Gegenteil, der geschickte Einsatz von Parallelität in TypeScript ermöglicht es uns, schnelle und reaktionsfähige Anwendungen zu erstellen.

\n\n

In diesem Artikel werden wir nicht nur Begriffe aufzählen. Wir werden untersuchen, wie async/await tatsächlich funktioniert, was unter der Haube des Event Loop steckt, ob man in TypeScript „Goroutinen“ wie in Go verwenden kann und wann man echte Threads über Web Workers einsetzen sollte. Los geht's!

\n\n

1. Asynchronität vs. Parallelität: Was ist der Unterschied?

\n\n

Bevor wir uns in den Code vertiefen, lassen Sie uns die Begriffe klären. Viele verwechseln diese Konzepte, aber das ist ein Fehler.

\n\n
    \n
  • Asynchronität (Nebenläufigkeit) – die Fähigkeit eines Programms, eine Aufgabe zu starten, zu einer anderen zu wechseln, während die erste wartet (z. B. auf eine Antwort vom Server), und später zu ihr zurückzukehren. Zu einem bestimmten Zeitpunkt wird nur eine Anweisung ausgeführt.
  • \n
  • Parallelität – die Ausführung von zwei oder mehr Aufgaben buchstäblich zur gleichen Zeit auf verschiedenen Prozessorkernen.
  • \n
\n\n

TypeScript bietet uns von Haus aus Asynchronität. Parallelität erfordert dagegen die Erstellung separater Threads – im Browser sind das Web Workers, in Node.js worker_threads. Merken Sie sich diesen Unterschied, sonst riskieren Sie, Code zu schreiben, der aufgrund der Blockierung des Hauptthreads „langsam“ wird.

\n\n

2. Event Loop und Callbacks – das Fundament der Parallelität in TypeScript

\n\n

Das Hauptgeheimnis der Asynchronität in JavaScript/TypeScript ist der Event Loop (Ereignisschleife). Stellen Sie sich vor, Sie haben eine Warteschlange von Aufgaben. Der Hauptthread nimmt eine Aufgabe, führt sie aus – wenn sie synchron ist, gibt er sofort das Ergebnis zurück. Handelt es sich um eine asynchrone Operation (z. B. setTimeout oder fetch), wird sie „zur Seite“ geschickt (in die Web API oder libuv), während der Hauptthread mit der Ausführung des nächsten Codes fortfährt.

\n\n

Wenn die asynchrone Operation abgeschlossen ist, gelangt ihr Callback in die Warteschlange (Task Queue oder Microtask Queue). Der Event Loop überprüft ständig, ob der Call-Stack leer ist, und wenn dies der Fall ist, nimmt er die erste Aufgabe aus der Warteschlange und führt sie aus.

\n\n
// Ein einfaches Beispiel, das dies demonstriert\nconsole.log('1. Start');\n\nsetTimeout(() => {\n    console.log('3. Timer ausgelöst');\n}, 0);\n\nPromise.resolve().then(() => {\n    console.log('2. Mikrotask ausgeführt');\n});\n\nconsole.log('4. Ende');\n\n// Ausgabe: 1, 4, 2, 3
\n\n

Beachten Sie: Das Promise (Mikrotask) wurde vor dem setTimeout (Makrotask) ausgeführt, obwohl die Verzögerung des Timers 0 ms beträgt. Dies ist ein wichtiges Wissen für die Optimierung von TypeScript.

\n\n

3. Async/Await – Zucker, der alles verändert

\n\n

Mit dem Aufkommen von ES2017 teilte sich die JS-Welt in ein „Vorher“ und „Nachher“. Async/await ist syntaktischer Zucker für Promises, der asynchronen Code wie synchronen Code aussehen lässt. Es fügt keine Threads hinzu, verbessert aber die Lesbarkeit radikal.

\n\n
// Funktion, die eine Verzögerung simuliert\nfunction delay(ms: number): Promise<void> {\n    return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n// Asynchrone Funktion mit async/await\nasync function fetchUserData(userId: number): Promise<string> {\n    console.log(`Fordere Daten von Benutzer ${userId} an...`);\n    await delay(2000); // Simulation einer Netzwerkanfrage\n    return `Daten von Benutzer ${userId}`;\n}\n\nasync function main() {\n    console.log('Start');\n    const user1 = await fetchUserData(1);\n    console.log(                

Blogs

Buchempfehlungen