Multithreading in JavaScript: So funktionieren Web-Worker
JavaScript ist dafür bekannt, dass alle Befehle sequentiell ausführt werden – und das single-threaded. Das bedeutet, dass bei der Entwicklung von JavaScript-Anwendungen und Websites nur ein CPU-Kern für die Ausführung des Codes zur Verfügung steht. Mit einem als Web-Worker bezeichneten Feature ist es möglich, JavaScript auf mehrere CPU-Kerne zu verteilen, was in diesem Fall die Auslagerung komplexer Operationen an einen separaten Thread bedeutet.
Mit Hilfe von Techniken wie setTimeout()
und setInterval()
sowie XHR-Requests lässt sich zwar eine Parallelität erzielen, sodass mehrere Operationen durchgeführt werden können, ohne einander zu blockieren. Diese Aufgaben werden aber weiterhin sequentiell – also nacheinander – im Event-Loop des Browsers abgearbeitet.
Web-Worker: Multithreading für JavaScript
Mit Web-Workern kannst du ein rechenintensives Skript ausführen, ohne die Ausführung anderer Skripte, die beispielsweise auf Benutzerinteraktionen reagieren, zu behindern. Die Applikation friert nicht ein und die Benutzer haben nicht mehr mit Fehlermeldungen wie „Ein Skript auf dieser Webseite braucht zu lange für die Ausführung“ zu kämpfen.
So kann trotz komplexer Aufgaben eine leistungsstarke und reaktionsschnelle Oberfläche für JavaScript-Anwendungen realisiert werden.
So setzt du Web-Worker ein
Web-Worker werden in einem isolierten Thread ausgeführt und müssen in einer separaten Datei angelegt werden. Um einen Web-Worker erzeugen zu können, benötigst du zunächst ein Worker-Objekt.
var worker = new Worker('worker.js');
Sofern die angegebenen Datei vorhanden ist, erzeugt der Browser einen neuen Worker-Thread.
Mit der postMessage()
-Methode startest du den Web-Worker.
worker.postMessage();
Kommunikation zwischen Web-Worker und Applikation
Die Kommunikation zwischen dem Web-Worker und der Hauptapplikation erfolgt über die postMessage()
-Methode und „Event-Listener“. Je nach Browser akzeptiert der Web-Worker eine einfache Zeichenfolge oder ein JSON-Objekt.
Das nachfolgende Beispiel zeigt, wie eine Kommunikation zwischen Web-Worker und Applikation stattfinden kann:
var worker = new Worker('doWork.js');
worker.addEventListener('message', function(e) {
console.log('Worker said: ', e.data);
}, false);
worker.postMessage('Hello World'); // Send data to our worker.
Code des Workers:
self.addEventListener('message', function(e) {
self.postMessage(e.data);
}, false);
Ein wenig abgewandelt, funktioniert das Beispiel auch mit der Übergabe von JSON-Objekten.
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
console.log(e.data);
}, false);
worker.postMessage({'cmd': 'TEST', 'msg': 'Hallo Welt'}); // Send data to our worker.
self.addEventListener('message', function(e) {
self.postMessage("Command: "+e.data.cmd);
self.postMessage("Message: "+e.data.msg);
}, false);
Einschränkungen für Web-Worker
Da Web-Worker parallel zum Hauptskript ausgeführt werden, haben sie keinen Zugriff auf die folgenden Funktionen von JavaScript.
- DOM
window
-Objektdocument
-Objektparent
-Objekt
Weiterhin zur Verfügung stehen:
navigator
-Objektlocation
-Objekt- XMLHttpRequests
setTimeout()
-MethodesetInterval()
-MethodeclearTimeout()
-MethodeclearInterval()
-MethodeimportScripts()
-Funktion- Das Erzeugen weiterer Web-Worker
Externe Skripte und untergeordnete Web-Worker
Mit der importScripts()
-Funktion können innerhalb von Web-Workern externe Skripte dazugeladen werden. Mehrere Skripte können mit Hilfe eines Kommas voneinander getrennt werden.
importScripts('script1.js');
importScripts('script2.js');
importScripts('script1.js','script2.js');
Neben dem Import externer Skripte sind Web-Worker auch in der Lage, Sub-Web-Worker zu erzeugen und so weitere Threads für verschiedene Aufgaben zu nutzen. Wie das funktioniert, kannst du in der Web-Worker-Spezifikation nachlesen.
Anwendungsfälle für Web-Worker
Zwar wird die Leistungsfähigkeit moderner JavaScript-Engines immer besser, irgendwann stoßen aber auch die besten Engines an ihre Grenzen. Web-Worker weiten die technischen Beschränkungen von JavaScript aus und machen parallele Verarbeitung möglich. Mögliche Einsatzszenarien für Web-Worker sind Echtzeitanwendung die zum Zeitpunkt der Benutzereingabe tätig werden – zum Beispiel eine Rechtschreibprüfung oder andere Methoden zur Textverarbeitung.
Auch für das Vorladen und Vorverarbeiten von Daten, die zu einem späteren Zeitpunkt in einer Applikation benötigt werden, sind Web-Worker eine leistungsstarke Methode. Sie eignen sich aber auch für jegliche weitere komplexe Rechenarbeiten, die beispielsweise bei der Arbeit mit Bildern, Video und Ton anfallen können.
Einige Beispiele für Web-Worker findest du hier:
- Web Worker Demo (Gegenüberstellung) – html5rocks
- Web Worker Demo (Simulated annealing) – mozilla.org
- Web Worker Demo (Animation und Berechnung) – html5demos.com
Weitere Informationen zur Web-Worker-Spezifikation gibt es hier:
- Web Worker-Spezifikation
- Using Web workers – Mozilla Developer Network
- Web Workers Rise Up! – Opera Dev
Hallo,
danke für den Artikel.
Zwei kleine Fehler haben sich eingeschlichen :)
Im ersten Bereich ist setTImeout falsch, großes I muss kleines i sein.
Im dritten Bereich stimmen die Apostroph nicht: var worker = new Worker(‚worker.js‘); richtig ist: var worker = new Worker(‚worker.js‘);
Für die Entwicklung ist es sehr wichtig, dass solche Sachen richtig geschrieben werden.
Gruß
Hallo Simon,
danke für den Hinweis. Habe das mal korrigiert.
Viele Grüße
Ilja
Hallo zusammen,
Danke für den netten Artikel. Ist zwar schon ein halbes Jahrzehnt her, aber ich denke es könnte sich lohnen auf den folgenden Artikel zu verweisen:
https://dassur.ma/things/is-postmessage-slow/
Da geht es um die Frage, ob postMessage langsam ist, warum es das manchmal sein kann (Stichwort: serializing) und wie man dieses Problem beheben kann (bspw. chunking).
LG,
Waldemar