Es ist bekannt, dass JavaScript in einem einzelnen Thread ausgeführt wird. Dieses Konzept ist so integral, dass wir es oft namentlich nennen – den Hauptthread. Bedeutet das also, dass Ihre Multi-Core-CPU beim Ausführen von JavaScript nutzlos ist?
Nicht ganz.
JavaScript kann etwas frech sein und gemischte Nachrichten über seine Threads senden.
Aber auch ohne sich mit komplexeren Funktionen wie Web Workers und WebAssembly zu befassen, kann einfaches JavaScript Multithread-Workflows nutzen.
Um es klarzustellen: Dieser Artikel konzentriert sich auf webbrowserbasiertes JavaScript. Andere Umgebungen wie Node.js handhaben die Dinge möglicherweise anders.
Wir werden Optionen für Multi-Thread- und Multi-Thread-ähnliche Operationen in JavaScript untersuchen. Die Realität ist: Sie können und sollten Multithread-Workflows in einfachem, reinem Vanilla-JavaScript nutzen.
Gerne.
JavaScript ist eine Single-Thread-Sprache, wird aber von Webbrowsern interpretiert – und Browser sind Multi-Thread-Software.
Es gibt verschiedene Methoden, um Multithreading zu simulieren und zu nutzen, und Sie verwenden sie möglicherweise, ohne es zu merken.
Darüber hinaus verhält sich JavaScript oft auf eine Art und Weise, die den Eindruck von Multithreading erweckt, obwohl es das tatsächlich nicht ist. Es hört sich vielleicht nach Betrug an, bietet aber tatsächlich die Vorteile, die wir von Multithread-Verhalten erwarten.
JavaScript ist ein ereignisbasiertes System. Wenn der Browser eine JavaScript-Datei verarbeitet, führt er den gesamten darin enthaltenen Code aus. Es ist durchaus möglich, Code zu schreiben, der nicht aufhört zu laufen – obwohl das im Allgemeinen eine schlechte Idee ist, weil es den Browser zum Absturz bringen kann.
Normalerweise richten wir Ereignis-Listener ein und warten darauf, dass etwas passiert. Zu diesem Zeitpunkt ist JavaScript völlig inaktiv. Dieses müßige Warten wäre nicht möglich, ohne etwas Arbeit aus dem Haupt-Thread auszulagern.
Ereignisse selbst sind nicht multithreaded, aber das Warten und Überprüfen auf Ereignisse wird vom Browser und nicht von JavaScript übernommen. Dieser Prozess wird vom Hauptthread ausgelagert.
Funktionen wie setTimeout und setInterval gibt es schon seit vielen Jahren bei uns. Sie entlasten den Hauptthread von der Wartezeit.
Lassen Sie uns die Funktionsweise vereinfachen. In niedrigeren Sprachen werden Timer und Warten oft durch die ständige Überprüfung implementiert: „Ist es schon Zeit?“ Stellen Sie sich eine while()-Schleife vor, die die verstrichene Zeit überprüft und zum richtigen Zeitpunkt fortfährt. Moderne Ansätze verwenden möglicherweise CPU-Interrupts, um ein Verstopfen des CPU-Threads zu vermeiden.
Wenn Sie jedoch setTimeout oder setInterval in JavaScript verwenden, ist die Sprache während des Wartens völlig inaktiv, was wiederum bedeutet, dass das Warten irgendwie außerhalb des Hauptthreads verarbeitet wird.
Heutzutage erhalten wir immer mehr asynchrone Funktionen und APIs von Browsern. Die Fetch-API ist ein bekanntes Beispiel. Auch neuere APIs, wie die Clipboard-API, sind asynchron.
Das bedeutet, dass Sie den Browser auffordern, eine Aktion auszuführen, und dieser den Haupt-Thread von JavaScript freigibt (oder ihm die Verarbeitung anderer Dinge ermöglicht – mehr dazu später) und Sie benachrichtigt (durch die Verarbeitung des folgenden Codes), wenn die Aktion abgeschlossen ist.
Asynchron bedeutet nicht unbedingt Multithreading – nicht immer, nicht direkt. Einfach das Wort „async“ vor eine Funktion zu setzen, bringt dir nichts.
Es gibt zwei Schlüsselkomponenten dafür, wie JavaScript mit asynchronem Verhalten umgeht:
Stellen Sie sich die Rückrufwarteschlange als eine Reihe von Funktionen vor, die darauf warten, ausgeführt zu werden. Diese Funktionen werden nacheinander im Hauptthread ausgeführt. Wenn ein asynchroner Vorgang abgeschlossen ist – sei es ein Ereignis, eine Zeitüberschreitung oder ein abgeschlossener Abruf – wird sein Rückruf in diese Warteschlange gestellt.
Die Ereignisschleife ist dafür verantwortlich, Rückrufe aus der Warteschlange in den Aufrufstapel zu verschieben, wenn der Aufrufstapel leer ist.
Der Webbrowser übernimmt die Kontrolle über bestimmte Vorgänge und erstellt möglicherweise neue Threads für diese – dazu gehören Aktionen wie fetch oder setTimeout. JavaScript muss diese Threads nicht verwalten. Wenn der asynchrone Vorgang abgeschlossen ist, stellt der Browser den entsprechenden Rückruf in die Rückrufwarteschlange und JavaScript setzt die Ausführung von dort aus fort.
Beachten Sie diesen Code:
console.log('Start'); setTimeout(() => { console.log('This is asynchronous'); }); console.log('End');
Ausgabe:
Start End This is asynchronous
Das passiert:
Beachten Sie, dass setTimeout der Timeout-Parameter fehlt. Dies ist eine gängige Technik, um die Ausführung von Code mit niedrigerer Priorität zu verzögern – verarbeiten Sie zuerst alles andere, und führen Sie dann die Sache mit der „niedrigeren Priorität“ aus.
Dadurch wird die Funktion innerhalb von setTimeout sofort in die Rückrufwarteschlange gestellt und bei der ersten Gelegenheit ausgeführt, wenn der Hauptthread inaktiv ist.
requestAnimationFrame ist eine Browser-API, die dem Browser mitteilt, dass Sie Code ausführen möchten, kurz bevor er die Ansicht berechnet und aktualisiert (stellen Sie sich das in Videospielen als Bilder pro Sekunde vor).
Aus irgendeinem Grund denken Webentwickler nicht an Repaints und FPS, aber so funktioniert es.
Es ist von Natur aus asynchron (jedoch nicht multithreaded).
Die Verwendung von setTimeout ohne Angabe einer Zeit wurde vor der Einführung von requestAnimationFrame oft für ähnliche Zwecke verwendet, obwohl es bei weitem nicht so nützlich war (einfach besser als nichts).
Diese Funktion stellt Ihren Code in eine Warteschlange in einem speziellen Stapel. Kurz vor dem Rendern eines neuen Frames (dem Repaint) führt der Browser diesen Code in der Warteschlange aus. Es ist perfekt, um Änderungen am DOM, HTML oder CSS vorzunehmen (und bitte nur diese, ich verbiete Ihnen, dort Berechnungen durchzuführen!).
Auch wenn es in diesem Artikel nicht um sie geht, ist es erwähnenswert, dass Sie echte Multithread-Operationen erreichen können, indem Sie Web Worker und WebAssembly verwenden. Damit können Sie Skripte in Hintergrundthreads ausführen. Sie können das DOM nicht direkt manipulieren, sind aber für die Verarbeitung schwerer Aufgaben (Matrixberechnungen, irgendjemand?) von unschätzbarem Wert. Dieser Ansatz wird häufig in HTML5-basierten Spielen verwendet – den komplexeren, nicht nur Tic-Tac-Toe.
Wenn JavaScript isoliert ist, bleibt es Single-Threaded. In der realen Welt handelt es sich jedoch um eine interpretierte Sprache, die in Multithread-Umgebungen von Webbrowsern ausgeführt wird. Wenn Ihr Dolmetscher Ihnen eine helfende Hand anbietet, nehmen Sie sie an und nutzen Sie sie.
Dieser Artikel deckt viel ab – Grundwissen, einfache Erklärungen und mehr. Aber so ist die Entwicklung oft – alles ist miteinander verbunden. Deshalb betrachte ich gerne das Gesamtbild; Manchmal ist eine detaillierte Fokussierung nicht die Lösung eines Problems.
Hat Ihnen Async schon einmal Alpträume beim Debuggen bereitet?
Das habe ich auf jeden Fall – vor einigen Jahren war es keine große Sache, Stacks mit Fehlern zu bekommen.
Das obige ist der detaillierte Inhalt vonNutzen Sie Multi-Thread in Javascript – weder über WebWorker noch über WebAssembly. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!