359 Stimmen

Webworker ohne separate Javascript-Datei?

Soweit ich das beurteilen kann, müssen Web Worker in einer separaten JavaScript-Datei geschrieben und wie folgt aufgerufen werden:

new Worker('longrunning.js')

Ich verwende den Closure-Compiler, um meinen gesamten JavaScript-Quellcode zusammenzufassen und zu verkleinern, und ich möchte meine Worker nicht in separaten Dateien verteilen müssen. Gibt es eine Möglichkeit, dies zu tun?

new Worker(function() {
    //Long-running work here
});

Angesichts der Tatsache, dass erstklassige Funktionen für JavaScript so wichtig sind, stellt sich die Frage, warum die Standardmethode für die Arbeit im Hintergrund eine weitere JavaScript-Datei vom Webserver laden muss.

294voto

vsync Punkte 101339

http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers

Was ist, wenn Sie Ihr Worker-Skript "on the fly" erstellen oder eine in sich geschlossene Seite erstellen möchten, ohne separate Worker-Dateien erstellen zu müssen? Mit Blob() können Sie Ihr Worker-Skript in dieselbe HTML-Datei wie Ihre Hauptlogik "einbinden", indem Sie ein URL-Handle zum Worker-Code als String erstellen

Vollständiges Beispiel für BLOB Inline Worker:

<!DOCTYPE html>
<script id="worker1" type="javascript/worker">
  // This script won't be parsed by JS engines because its type is javascript/worker.
  self.onmessage = function(e) {
    self.postMessage('msg from worker');
  };
  // Rest of your worker code goes here.
</script>
<script>
  var blob = new Blob([
    document.querySelector('#worker1').textContent
  ], { type: "text/javascript" })

  // Note: window.webkitURL.createObjectURL() in Chrome 10+.
  var worker = new Worker(window.URL.createObjectURL(blob));
  worker.onmessage = function(e) {
    console.log("Received: " + e.data);
  }
  worker.postMessage("hello"); // Start the worker.
</script>

196voto

Adria Punkte 8078

Die Lösung von html5rocks, den Webworker-Code in HTML einzubetten, ist ziemlich grausam.
Und ein Blob von escaped JavaScript-as-a-string ist nicht besser, nicht zuletzt, weil es kompliziert Work-Flow (Closure Compiler kann nicht auf Strings).

Ich persönlich mag die toString-Methoden sehr, aber @dan-man DIESE Regex!

Mein bevorzugter Ansatz:

// Build a worker from an anonymous function body
var blobURL = URL.createObjectURL( new Blob([ '(',

function(){
    //Long-running work here
}.toString(),

')()' ], { type: 'application/javascript' } ) ),

worker = new Worker( blobURL );

// Won't be needing this anymore
URL.revokeObjectURL( blobURL );

Die Unterstützung ist die Schnittmenge dieser drei Tabellen:

Das funktioniert nicht bei einer SharedWorker da die URL eine exakte Übereinstimmung sein muss, auch wenn der optionale Parameter "Name" übereinstimmt. Für einen SharedWorker benötigen Sie eine separate JavaScript-Datei.


Update 2015 - Die ServiceWorker-Singularität ist da

Jetzt gibt es eine noch bessere Möglichkeit, dieses Problem zu lösen. Auch hier speichern Sie den Worker-Code als Funktion (statt als statische Zeichenkette) und konvertieren ihn mit .toString(). Dann fügen Sie den Code in CacheStorage unter einer statischen URL Ihrer Wahl ein.

// Post code from window to ServiceWorker...
navigator.serviceWorker.controller.postMessage(
 [ '/my_workers/worker1.js', '(' + workerFunction1.toString() + ')()' ]
);

// Insert via ServiceWorker.onmessage. Or directly once window.caches is exposed
caches.open( 'myCache' ).then( function( cache )
{
 cache.put( '/my_workers/worker1.js',
  new Response( workerScript, { headers: {'content-type':'application/javascript'}})
 );
});

Es gibt zwei mögliche Rückzugsmöglichkeiten. ObjectURL wie oben, oder noch nahtloser, indem man eine real JavaScript-Datei unter /my_workers/worker1.js

Die Vorteile dieses Ansatzes sind:

  1. SharedWorkers können ebenfalls unterstützt werden.
  2. Registerkarten können sich eine einzige Kopie im Cache unter einer festen Adresse teilen. Beim Blob-Ansatz werden zufällige objectURLs für jede Registerkarte erzeugt.

38voto

Delan Azabani Punkte 76072

Sie können eine einzelne JavaScript-Datei erstellen, die ihren Ausführungskontext kennt und sowohl als übergeordnetes Skript als auch als Worker fungieren kann. Beginnen wir mit einer Grundstruktur für eine solche Datei:

(function(global) {
    var is_worker = !this.document;
    var script_path = is_worker ? null : (function() {
        // append random number and time to ID
        var id = (Math.random()+''+(+new Date)).substring(2);
        document.write('<script id="wts' + id + '"></script>');
        return document.getElementById('wts' + id).
            previousSibling.src;
    })();
    function msg_parent(e) {
        // event handler for parent -> worker messages
    }
    function msg_worker(e) {
        // event handler for worker -> parent messages
    }
    function new_worker() {
        var w = new Worker(script_path);
        w.addEventListener('message', msg_worker, false);
        return w;
    }
    if (is_worker)
        global.addEventListener('message', msg_parent, false);

    // put the rest of your library here
    // to spawn a worker, use new_worker()
})(this);

Wie Sie sehen können, enthält das Skript den gesamten Code sowohl für die Sicht des Elternteils als auch für die Sicht des Arbeiters und prüft, ob seine eigene individuelle Instanz ein Arbeiter ist mit !document . Die etwas unhandliche script_path Berechnung wird verwendet, um den Pfad des Skripts relativ zur übergeordneten Seite genau zu berechnen, da der Pfad, der an new Worker bezieht sich auf die übergeordnete Seite, nicht auf das Skript.

30voto

dan-man Punkte 2671

を使用しています。 Blob Methode, wie wäre es mit dieser für eine Arbeiterfabrik:

var BuildWorker = function(foo){
   var str = foo.toString()
             .match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1];
   return  new Worker(window.URL.createObjectURL(
                      new Blob([str],{type:'text/javascript'})));
}

Man könnte es also so verwenden...

var myWorker = BuildWorker(function(){
   //first line of worker
   self.onmessage(){....};
   //last line of worker
});

EDIT:

Ich habe diese Idee weiter ausgebaut, um die Kommunikation zwischen verschiedenen Threads zu erleichtern: bridged-worker.js .

EDIT 2:

Der obige Link führt zu einem von mir erstellten Gist. Jemand anderes hat ihn später in eine aktuelle Repo .

12voto

GG. Punkte 19294

Aktuelle Antwort (2018)

Sie können verwenden Greenlet :

Verschieben einer asynchronen Funktion in ihren eigenen Thread. Eine vereinfachte Einzelfunktionsversion von Workerize .

Beispiel:

import greenlet from 'greenlet'

const getName = greenlet(async username => {
  const url = `https://api.github.com/users/${username}`
  const res = await fetch(url)
  const profile = await res.json()
  return profile.name
})

console.log(await getName('developit'))

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X