Web Worker

Web Worker erlauben es, in JavaScript Code getrennt vom Hauptthread im Hintergrund auszuführen. Web Worker gibt es in drei Typen: Dedicated Workers, Shared Workers und Service Workers.

Dedicated Workers

Der gewöhnliche Web Worker nennt sich Dedicated Worker.

Problem

Ein Dialogfeld mit dem Hinweis, dass ein Skript nicht mehr antwortet und der Frage, ob es beendet werden soll.
Warnmeldung über ein Skript, das so lange braucht, dass es nicht mehr zu reagieren scheint

Traditionell wurden JavaScript-Skripte von Browsern nur in einem einzigen Thread im Vordergrund ausgeführt. Dies führt zu Problemen, wenn aufwändiger Code verarbeitet werden soll, da dieser möglicherweise sehr lange läuft und damit in der Zwischenzeit die Interaktion des Benutzers mit der Internetseite erschwert oder ganz unmöglich macht. Daher bieten die meisten Browser eine Möglichkeit ein lange laufendes Skript abzubrechen. Doch auch in diesem Fall kann der Browser für kurze Zeit durch ein Skript blockiert werden, zudem bereitet das Zeitlimit Skripten ein Problem, wenn diese eine komplexe und damit rechenintensive Aufgabe bearbeiten sollen. Um diesen Problemen zu begegnen, wurde die Web-Worker-API entwickelt.[1]

Web-Worker-API

Über new Worker( 'skript.js' ) kann ein neuer Web Worker eingerichtet werden. Der Browser lädt dazu das angegebene Skript in einen neuen Thread. Die Kommunikation mit dem Worker kann in beide Richtungen über die Methode postMessage und den Eventlistener onmessage erfolgen.

Innerhalb des Workers stehen alle JavaScript-Funktionen zur Verfügung, die im ECMAScript-Standard definiert sind, ebenso viele der browsertypischen APIs. Eine wichtige Ausnahme hiervon ist das Document Object Model. Der Worker-Code hat hierauf keinen Zugriff und muss zum Verändern der Seite daher dem Hauptthread eine Nachricht schicken, damit dieser die gewünschten Befehle ausführt.

Variablenübergabe

Über postMessage können Variablen von einfachen Typen übergeben werden, Zahlen, Zeichenketten, Arrays und einfache Objekte (etwas mehr als in JSON möglich ist). Anders als bei Übergaben an Funktionen wird dabei eine Kopie angefertigt, die Daten also als Wertparameter und nicht wie sonst üblich als Referenzparameter übergeben. Funktionen und Objekte im Sinne von objektorientiertem Code müssen dagegen für eine Übergabe geeignet serialisiert und deserialisiert werden.

Binäre Daten können als ArrayBuffer übergeben werden, anschließend ist aber kein Zugriff mehr auf das Original möglich. Alternativ kann ein SharedArrayBuffer verwendet werden, der von mehreren Threads gleichzeitig bearbeitet werden kann. Hierfür stehen besondere atomare Operationen zur Verfügung.[2]

Während DOM-Elemente nicht an einen anderen Thread übergeben werden können, soll dies für Canvas-Elemente bis zu einem gewissen Maß möglich gemacht werden. Der Hauptthread kann über ein OffscreenCanvas-Element die Kontrolle an einen Hintergrundthread abgeben, dort können alle üblichen Funktionen zum Zeichnen aufgerufen werden.[3]

Beispiel

Das folgende Beispiel sucht nach Primzahlen. Diese werden der Reihe nach in einem Worker berechnet und dem Hauptthread zur Anzeige übergeben.[4]

Das HTML-Dokument bettet dabei direkt das Hauptskript ein. Dieses erstellt einen Worker und reagiert anschließend auf Mitteilungen, die dieser Worker sendet.

<!DOCTYPE html>
<html>
 <head>
  <title>Worker-Beispiel</title>
 </head>
 <body>
  <p>Die bis jetzt größte gefundene Primzahl ist: <output id="result"></output></p>
  <script>
   var worker = new Worker( 'worker.js' ); // erzeugt neuen Worker
   worker.onmessage = function ( event ) { // wird aufgerufen, wenn Worker eine Nachricht sendet
     document.getElementById( 'result' ).textContent = event.data; // zeigt die gefundene Primzahl an
   };
  </script>
 </body>
</html>

Der Code für das Skript worker.js sucht dabei in einer Endlosschleife nach Primzahlen und teilt dem Hauptthread immer mit, wenn er eine gefunden hat:

var i, n = 1;
search: while ( true ) {
  n++;
  for ( i = 2; i <= Math.sqrt( n ); i++ ) {
    if ( n % i === 0 ) {
      continue search;
    }
  }
  // n ist eine Primzahl
  postMessage( n );
}

Shared Worker

Ein gewöhnlicher Worker gehört immer zu der Seite, die ihn erzeugt hat. Ruft ein Benutzer die Seite mehrfach in seinem Browser auf (oder verschiedene Seiten derselben Domain, die den gleichen Worker einsetzen), so werden mehrere Worker mit dem gleichen Code initialisiert. Setzt man stattdessen einen SharedWorker ein, so wird dieser wiederverwendet.

Service Worker

Eine weitere besondere Art eines Web Workers ist der Service Worker. Dieser wird für zwei Aufgaben eingesetzt: Zum einen kann er als Proxy fungieren, zum anderen vom Server gesendete Benachrichtigungen selbst dann empfangen, wenn gerade keine Seite der entsprechenden Domain geöffnet ist.[5]

Für die Aufgabe als Proxy steht im Worker das fetch-Event zur Verfügung, das immer ausgelöst wird, wenn der Browser Daten der überwachten Domain anfordert, unabhängig davon, ob dies durch AJAX, Benutzernavigation oder andere Ursachen ausgelöst wird. Der Worker kann das Event abfangen und bei Bedarf die angeforderten Daten auf andere Weise zur Verfügung stellen. Insbesondere sind Service Worker als Ersatz für Application Cache zur Implementierung von Web-Apps vorgesehen, die auch offline funktionieren sollen.[6] Die Spezifikation ist aber so flexibel, dass zahlreiche weitere Möglichkeiten über einen einfachen Cache hinaus möglich sind.[7]

Browserunterstützung

Alle gängigen Browser bieten zumindest grundlegende Unterstützung für Web Worker, Mozilla Firefox ab Version 3.5, Google Chrome ab der Version 4, der Internet Explorer ab Version 10, Edge ab Version 12.[8] Die Unterstützung wurde und wird mit neueren Versionen immer weiter ausgebaut. So stehen Service Worker in Firefox ab Version 44, in Chrome ab Version 40 zur Verfügung und sind auch in diesen Browsern noch nicht im kompletten Umfang implementiert.[9]

Normen und Standards

Web Workers ist standardisiert vom W3C. Die aktuelle Spezifikation stammt vom 24. September 2015.

Einzelnachweise

  1. Intensive JavaScript. MDN Web Docs. Abgerufen am 27. Juni 2016.
  2. Lars T Hansen: A Taste of JavaScript’s New Parallel Primitives. Veröffentlicht am 5. Mai 2016 in Mozilla Hacks, abgerufen am 27. Juni 2016.
  3. Nick Desaulniers: WebGL Off the Main Thread. Veröffentlicht am 22. Januar 2016 in Mozilla Hacks, abgerufen am 27. Juni 2016.
  4. Beispiel adaptiert von: Ian Hickson: Web Workers. W3C-Draft vom 24. September 2015.
  5. Dan Callahan: Web Push Arrives in Firefox 44. Veröffentlicht am 26. Januar 2016 in Mozilla Hacks, abgerufen am 27. Juni 2016.
  6. Service Workers. Motivation. W3C-Draft vom 25. Juni 2015.
  7. Salva: Beyond Offline. Veröffentlicht am 21. Dezember 2015 in Mozilla Hacks, abgerufen am 27. Juni 2016.
  8. Can I use: Web Workers. Abgerufen am 27. Juni 2016.
  9. Is ServiceWorker ready? Abgerufen am 27. Juni 2016.