Webworker ab IE 10 Chrome 11 http://www.w3.org/TR/workers/ Beispiel 1 ---------- Das folgende Beispiel ist etwas komplexer. Darin werden Nachrichten mithilfe von JSON-Objekten weitergegeben. Hauptskript: doWork2.js: self.addEventListener('message', function(e) { var data = e.data; switch (data.cmd) { case 'start': self.postMessage('WORKER STARTED: ' + data.msg); break; case 'stop': self.postMessage('WORKER STOPPED: ' + data.msg + '. (buttons will no longer work)'); self.close(); // Terminates the worker. break; default: self.postMessage('Unknown command: ' + data.msg); }; }, false); Hinweis: Ein Worker kann auf zwei Arten beendet werden: durch Aufrufen von worker.terminate() über die Hauptseite oder durch Aufrufen von self.close() im Worker selbst. Beispiel: Führen Sie diesen Worker aus! Im Zusammenhang mit Workern weisen self und this auf den globalen Geltungsbereich des Workers hin. Daher könnte das vorherige Beispiel auch wie folgt geschrieben werden: addEventListener('message', function(e) { var data = e.data; switch (data.cmd) { case 'start': postMessage('WORKER STARTED: ' + data.msg); break; case 'stop': ... }, false); Alternativ könnten Sie den Ereignis-Handler onmessage direkt festlegen, obwohl addEventListener von JavaScript-Ninjas empfohlen wird. onmessage = function(e) { var data = e.data; ... }; Überlegungen zum gleichen Ursprung Bei Worker-Skripts muss es sich um externe Dateien handeln, die das gleiche Schema wie die aufrufende Seite aufweisen. Daher können Sie ein Skript nicht von einer data:- oder javascript:-URL laden und eine https:-Seite kann keine Worker-Skripts starten, die mit http:-URLs beginnen. Asynchron vs. parallel Javascript kann Aufgaben asynchron via setTimeout(), setInterval(), XMLHttpRequest und in Event-Handlern durchführen, nicht aber parallel zu anderen Aufgaben. Ein Web Worker ist ein Script, dass Arbeiten im Hintergrund durchführt, ohne dabei die Benutzerschnittstelle zu blockieren. Eine App kann ein Backup auf dem Server anlegen oder einfach nur die Änderungen am Dokument für die History oder das Protokoll starten, ohne dass der Benutzer diese Hintergrundprozesse bemerkt. Von unseren Desktop-Rechnern kennen wir solche Dienstleistungen bereits. Javascript ist von Haus aus eine »Single Threaded« Programmiersprache und kann nicht zwei Aufgaben gleichzeitig durchführen. Es gibt kein »fork« wie z.B. in C. Man stelle sich nur einmal vor, ein Thread würde ein Element des DOM verbergen und ein anderer Thread will das Element anzeigen … So bleibt Javascript schön einfach und Datenstrukturen müssen nicht thread safe sein. Asynchron vs. parallel Javascript kann Aufgaben asynchron via setTimeout(), setInterval(), XMLHttpRequest und in Event-Handlern durchführen, nicht aber parallel zu anderen Aufgaben. Warum das nicht reicht? Cloud Computing kommt mit großen Schritten auf uns zu. Wir werden immer öfters unsere Dokumente direkt im Web bearbeiten. Ein XMLHttpRequest() reicht dann nicht mehr … Cloud Computing 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 Workers müssen für Zugriffe auf das DOM Nachrichten an den Haupt-Thread senden. Wenn ein Web Worker Änderungen am DOM wünscht, muss derWeb Worker eine Nachricht an den Haupt-Thread senden. 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 also 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 getDocumentById(), kein window.onload … setTimeout(), setInterval() und XMLHttpRequest() funktionieren aber auch in Web Workern. Aufgrund ihres Multithread-Verhaltens können Web Worker nur auf einen Teil der Funktionen von JavaScript zugreifen: •Das navigator-Objekt •Das location-Objekt (schreibgeschützt) •XMLHttpRequest •setTimeout()/clearTimeout() und setInterval()/clearInterval() •Den Anwendungscache •Import externer Skripts mithilfe der importScripts()-Methode •Erzeugen weiterer Web Worker Worker haben KEINEN Zugriff auf: •Das DOM (nicht threadsicher) •Das window-Objekt •Das document-Objekt •Das parent-Objekt Externe Skripts laden Mithilfe der importScripts()-Funktion können Sie externe Skriptdateien oder -bibliotheken in einen Worker laden. Bei dieser Methode können null oder mehr Zeichenfolgen als Dateinamen der zu importierenden Ressourcen angegeben werden. Bei diesem Beispiel werden script1.js und script2.js in den Worker geladen: worker.js: importScripts('script1.js'); importScripts('script2.js'); Dies lässt sich auch in einer Importanweisung zusammenfassen: importScripts('script1.js', 'script2.js'); Untergeordnete Worker Worker sind in der Lage, untergeordnete Worker zu erzeugen. So lassen sich große Aufgaben während der Laufzeit noch weiter unterteilen. Untergeordnete Worker haben jedoch auch einige Nachteile: •Beim Hosten untergeordneter Worker müssen Sie sich an der übergeordneten Seite orientieren. •URIs in untergeordneten Workern werden in Abhängigkeit vom Speicherort des übergeordneten Workers aufgelöst (im Gegensatz zur Hauptseite). Beachten Sie, dass die meisten Browser für jeden Worker separate Prozesse erzeugen. Bevor Sie eine Worker-Farm erzeugen, vergewissern Sie sich, dass Sie nicht zu viele Systemressourcen des Nutzers beanspruchen. Dies könnte unter anderem der Fall sein, weil Nachrichten, die zwischen der Hauptseite und den Workern ausgetauscht werden, kopiert und nicht gemeinsam genutzt werden. Inline-Worker Nehmen wir an, Sie möchten Ihr Worker-Skript spontan erstellen oder eine eigenständige Seite erzeugen, ohne separate Worker-Dateien erstellen zu müssen. Mit der neuen BlobBuilder-Oberfläche können Sie Ihren Worker in die gleiche HTML-Datei wir Ihre Hauptlogik "einbetten". Dazu erstellen Sie einen BlobBuilder und hängen den Worker-Code als Zeichenfolge an: // Prefixed in Webkit, Chrome 12, and FF6: window.WebKitBlobBuilder, window.MozBlobBuilder var bb = new BlobBuilder(); bb.append("onmessage = function(e) { postMessage('msg from worker'); }"); // Obtain a blob URL reference to our worker 'file'. // Note: window.webkitURL.createObjectURL() in Chrome 10+. var blobURL = window.URL.createObjectURL(bb.getBlob()); var worker = new Worker(blobURL); worker.onmessage = function(e) { // e.data == 'msg from worker' }; worker.postMessage(); // Start the worker. Fehlerbehebung Wie bei jeder JavaScript-Logik möchten Sie sicherlich zurückgegebene Fehler in Ihren Web Workern behandeln. Wenn beim Ausführen eines Workers ein Fehler auftritt, wird ein ErrorEvent ausgelöst. Die Oberfläche liefert drei nützliche Eigenschaften, mit deren Hilfe Sie der Sache auf den Grund gehen können: filename - der Name des Worker-Skripts, das den Fehler verursacht hat, lineno - die Nummer der Zeile, in der der Fehler aufgetreten ist, und message - eine nützliche Beschreibung des Fehlers. In diesem Beispiel zeige ich Ihnen, wie Sie einen onerror-Ereignis-Handler einrichten, um die Fehlereigenschaften auszudrucken: Beispiel: workerWithError.js versucht, 1/x durchzuführen, wobei x nicht definiert ist. Ein Web Worker ist ein Script, dass Arbeiten im Hintergrund durchführt, ohne dabei die Benutzerschnittstelle zu blockieren. Eine App kann ein Backup auf dem Server anlegen oder einfach nur die Änderungen am Dokument für die History oder das Protokoll starten, ohne dass der Benutzer diese Hintergrundprozesse bemerkt. Von unseren Desktop-Rechnern kennen wir solche Dienstleistungen bereits. Javascript ist von Haus aus eine »Single Threaded« Programmiersprache und kann nicht zwei Aufgaben gleichzeitig durchführen. Es gibt kein »fork« wie z.B. in C. Man stelle sich nur einmal vor, ein Thread würde ein Element des DOM verbergen und ein anderer Thread will das Element anzeigen … So bleibt Javascript schön einfach und Datenstrukturen müssen nicht thread safe sein. Asynchron vs. parallel Javascript kann Aufgaben asynchron via setTimeout(), setInterval(), XMLHttpRequest und in Event-Handlern durchführen, nicht aber parallel zu anderen Aufgaben. Warum das nicht reicht? Cloud Computing kommt mit großen Schritten auf uns zu. Wir werden immer öfters unsere Dokumente direkt im Web bearbeiten. Ein XMLHttpRequest() reicht dann nicht mehr … Cloud Computing 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 Workers müssen für Zugriffe auf das DOM Nachrichten an den Haupt-Thread senden. Wenn ein Web Worker Änderungen am DOM wünscht, muss derWeb Worker 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 also 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 getDocumentById(), kein window.onload … setTimeout(), setInterval() und XMLHttpRequest() funktionieren aber auch in Web Workern. Javascriptdocumentelementlocationnavigatorxhrattributwindowweb workerlocationnavigatorxhrself 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 oder App mit einem neu erzeugten Web Worker-Objekt übergeben: var 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. Web Worker starten Letzten Web Worker canceln 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 { var theWorker = new Worker ("web-worker.js"); theWorker.addEventListener ("message", onMessage, true); theWorker.addEventListener ("error", onError, true); // Daten, die wir an den Worker übergeben var 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 () { var 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);