Javascript Web Worker • Arbeiten im Hintergrund

Javascript Web Worker

Javascript ist von Haus aus eine Single Threaded Programmiersprache und kann nicht zwei Aufgaben gleichzeitig durchführen. Während der Ausführung friert alles ein: Menüs, Scrollen, Downloads, der Aufbau und das Rendern der Seite sowie Animationen.

Web Worker führen Arbeiten im Hintergrund durch, ohne dabei die Benutzerschnittstelle zu blockieren.

Wenn ein Script die Benutzerschnittstelle blockiert …

Da Scripte normalerweise erst ausgeführt werden, wenn die Seite vollständig geladen ist und die meisten Scripte schnell und nur in kleinen Stückchen ausgeführt werden, fallen kurze Blockaden kaum auf.

Javascript kann Aufgaben asynchron via setTimeout(), setInterval(), XMLHttpRequest oderAjax und in Event-Handlern durchführen, aber nicht parallel zu anderen Aufgaben. Eine Reihe von Javascript-Operationen wird allerdings immer synchron ausgeführt.

  • Berechnungen
  • DOM-Updates
  • Speichern und Lesen von Local Storage und IndexedDB

Dabei stottert die Benutzerschnittstelle (z.B. beim Scrollen) und langsame Geräte zeigen u.U. Seite antwortet nicht oder Script unresponsive. Es gibt kein fork wie z.B. in C. So bleibt Javascript einfach und Datenstrukturen müssen nicht thread safe sein.

Web Worker – parallele Verarbeitung

Web Worker sind ein Kompromiss – sie laufen parallel zum Haupt-Thread, aber sie dürfen nicht auf das DOM zugreifen. Web Worker müssen für Zugriffe auf das DOM Nachrichten an den Haupt-Thread senden.

Wenn ein Worker Änderungen am DOM wünscht, muss er eine Nachricht an den Haupt-Thread senden.

Das Web Worker-Script

Ein Web Worker braucht eine Javascript-Datei auf dem Server, das die Arbeiten im Hintergrund durchführt. Der Web Worker läuft also nicht im Browserfenster – ein Zugriff auf window wird demnach einen Fehler liefern. In einem Web Worker gibt es nur self. Web Worker haben keinen Zugriff auf das DOM – das kann gar nicht oft genug gesagt werden: kein getElementById(), kein window.onload … 

setTimeout(), setInterval() und XMLHttpRequest() funktionieren aber auch in Web Workern.

Web Worker Struktur

Es gibt kein Limit für die Anzahl der Web Worker, aber Web Worker stellen eine nicht unbeträchtliche Last dar und dürften nicht beliebig einsetzbar sein.

Die URL des Web Worker-Scripts wird der HTML-Seite mit einem neu erzeugten Web Worker-Objekt übergeben:

let theWorker = new Worker ("web-worker.js");

Web Worker Events

Web Worker haben zwei Events:

  • message schickt Daten vom Hintergrund-Thread zurück,
  • das error-Event des Web Workers gibt einen evtl. Fehler zurück.

Jeder Klick auf »Web Worker starten« tickt einen Web Worker an, der Web Worker gibt eine kurze Bestätigung und beginnt dann seine Arbeit im Hintergrund. Nach getaner Arbeit (nach 5 Sekunden) meldet sich der Webworker mit einer Antwort.

Der Haupt-Thread lauscht also auf Nachrichten vom Web Worker, aber zunächst schickt der Haupt-Thread eine Nachricht an den Web Worker.

function startWW () {
   if (typeof(Worker) === "undefined") {
      document.querySelector('#msg').innerHTML = 
      "Keine Unterstützung für Web Worker in diesem Browser";
   } else {
      let theWorker = new Worker ("web-worker.js");
      theWorker.addEventListener ("message", onMessage, true);
      theWorker.addEventListener ("error", onError, true);
      
      // Daten, die wir an den Worker übergeben
      let data = "Ein Hallo von Worker ";
      theWorker.postMessage(data);
   }
}

Web Worker Event Handler

Die Funktionen onMessage und onError in diesem Beispiel sind einfach gestrickt. onMessage gibt die Antwort eines Web Workers im Textfeld aus.

Event Handler für Nachrichten und Fehlermeldungen
function onMessage (e) {
   resArea.value += "Antwort: " + e.data + " …\n";
}

function onError (e) {
   resArea.value += "Fehler: [" + e.filename + ", Zeile " + e.lineno + "] " + e.message + "\n";
}

Web Worker Skriptfehler abfangen

Die Funktion onError fängt Fehlermeldungen des Web Workers ab und gibt mit lineno Syntax-Fehler des Webworkers zurück. Fehlermeldungen des Web Workers sehen in etwa so aus:

Fehler: [web-worker.js, Zeile 12] 
ReferenceError: Can't find variable: rabbo

Auch der Web Worker in diesem Beispiel ist einfach gestrickt: Das Web Worker-Script wartet auf eine zündende Nachricht vom Haupt-Thread, bearbeitet seine Aufgabe und sendet dann eine Nachricht an den Haupt-Thread.

Web Worker Script
// verbraucht einfach nur Zeit … reine Demo
function sleep () {
   let start = new Date ().getTime();
   while (new Date().getTime() < start + delay) ;
}

// Das ist der perfekte Job … sleep
function msgHandler (e) {
   postMessage ('Erst mal eine Runde schlafen');
   sleep (5000);
   postMessage (e.data);
}

// Der Web Worker horcht auf ein message-Event
addEventListener ("message", msgHandler, true);

WebWorker-Einsatz

Komplexe Berechnungen im Hintergrund sowie Lesen und Schreiben von localStorage und IndexedDB werden häufig als Beispiel für den Einsatz von Web Workern herangezogen. Web Worker können ein Backup des Dokuments im Hintergrund durchführen, 3D-Grafiken permanent rendern, während der Benutzer das 3D-Objekt im 3D-Workspace verschiebt.

Der Wordpress-Editor führt Backups still und leise durch, ohne den Benutzer zu stören – ob hier schon im Hintergrund Web Worker wirken, kann ich nicht sagen. Aber ein Backup im Hintergrund ist eine typische Aufgabe für einen Web Worker.
Web Worker Struktur Javascriptdocumentelementlocationnavigatorxhrattributwindowweb workerlocationnavigatorxhrself