Web-Worker ohne separate Javascript-Datei?

291

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

new Worker('longrunning.js')

Ich verwende den Closure-Compiler, um meinen gesamten JavaScript-Quellcode zu kombinieren und zu minimieren, und ich möchte meine Mitarbeiter lieber nicht in separaten Dateien für die Verteilung haben. 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, warum muss die Standardmethode für Hintergrundarbeiten eine ganze andere JavaScript-Datei vom Webserver laden?

Ben Dilts
quelle
7
Das liegt daran, dass es noch wichtiger ist, einen Ausführungskontext rein threadsicher zu halten als erstklassige Funktionen :-)
Pointy
1
Ich arbeite daran (oder vielmehr daran, das Problem zu minimieren): DynWorker . Sie können tun: var worker = new DynWorker(); worker.inject("foo", function(){...});...
Félix Saparelli
1
Das OP hat die Frage "Teaching Worker soll Funktion anstelle von JavaScript-Quelldatei akzeptieren" gelöscht. Die Antwort wird hier erneut veröffentlicht
Rob W
Ich habe task.js entwickelt , um dies viel einfacher zu machen. Meistens versuchen Sie nur, kleine Sperraufgaben auszulagern.
Chad Scira

Antworten:

225

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

Was ist, wenn Sie Ihr Worker-Skript im laufenden Betrieb erstellen oder eine eigenständige Seite erstellen möchten, ohne separate Worker-Dateien erstellen zu müssen? Mit Blob () können Sie Ihren Worker in derselben HTML-Datei wie Ihre Hauptlogik "einbinden", indem Sie ein URL-Handle für den Worker-Code als Zeichenfolge erstellen


Vollständiges Beispiel für einen 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>

vsync
quelle
Google Chrome einzige Lösung, scheint Firefox 10 wird es unterstützen, ich weiß nicht über andere Browser
4esn0k
2
BlobBuiler ist jetzt veraltet . Verwenden Sie stattdessen Blob . Derzeit in den neuesten Versionen von Firefox / WebKit / Opera und IE10 unterstützt, siehe Kompatibilitätstabellen für ältere Browser.
Félix Saparelli
3
Der Blob-Konstruktor wird möglicherweise in IE10 unterstützt, Sie können jedoch kein Javascript über ihn an den Web-Worker weitergeben (nicht einmal in IE11): connect.microsoft.com/IE/feedback/details/801810/… .
Jayarjo
1
@albanx -was testet? Es sind bereits Milliarden Demoseiten online, die zeigen, dass das Threading den Browser seit Jahren nicht mehr auflegt.
vsync
2
@albanx - Möchten Sie zumindest sagen, welchen esoterischen Browser Sie verwenden, der hängt? hängt diese Demo für Sie? ie.microsoft.com/testdrive/Graphics/WorkerFountains/…
vsync
162

Die html5rocks-Lösung zum Einbetten des Web-Worker-Codes in HTML ist ziemlich schrecklich.
Und ein Blob von maskiertem JavaScript als Zeichenfolge ist nicht besser, nicht zuletzt, weil es den Arbeitsablauf verkompliziert (der Closure-Compiler kann nicht mit Zeichenfolgen arbeiten).

Persönlich mag ich die toString-Methoden wirklich, aber @ dan-man DIESER 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 );

Unterstützung ist der Schnittpunkt dieser drei Tabellen:

Dies funktioniert jedoch nicht für einen SharedWorker , da die URL genau übereinstimmen 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 kommt an

Jetzt gibt es eine noch leistungsfähigere Möglichkeit, dieses Problem zu lösen. Speichern Sie den Worker-Code erneut als Funktion (anstelle einer statischen Zeichenfolge) und konvertieren Sie ihn mit .toString (). Fügen Sie dann den Code unter einer statischen URL Ihrer Wahl in CacheStorage 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ückschläge. ObjectURL wie oben oder nahtloser setzen eine echte JavaScript-Datei unter /my_workers/worker1.js ablegen

Vorteile dieses Ansatzes sind:

  1. SharedWorker können ebenfalls unterstützt werden.
  2. Registerkarten können eine einzelne zwischengespeicherte Kopie an einer festen Adresse freigeben. Der Blob-Ansatz vermehrt zufällige objectURLs für jede Registerkarte.
Adria
quelle
4
Wie würde die Browserkompatibilität bei dieser Lösung aussehen?
Ben Dilts
Können Sie diese Lösung näher erläutern, wie funktioniert sie? Was ist der worker1.js? Ist es eine separate JS-Datei? Ich versuche dies zu verwenden, kann es aber nicht zum Laufen bringen. Insbesondere versuche ich, es für einen SharedWorker
Yehuda
Wenn Sie es nur in eine nützliche Funktion einpacken könnten!
mmm
@ Ben Dilts: Browserkompatibilität würde so aussehen, als würde man seinen Code einfach über babel ausführen
Jack Giffin
Der Standard garantiert nicht, dass Function.prototype.toString () den Funktionskörper als Zeichenfolge zurückgibt. Sie sollten der Antwort wahrscheinlich eine Warnung hinzufügen.
RD
37

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

(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 aus Sicht des Elternteils als auch des Arbeitnehmers und prüft, ob es sich bei seiner eigenen Instanz um einen Arbeiter handelt !document. Die etwas unhandliche script_pathBerechnung wird verwendet, um den Pfad des Skripts relativ zur übergeordneten Seite genau zu berechnen, da der angegebene Pfad new Workerrelativ zur übergeordneten Seite und nicht zum Skript ist.

Delan Azabani
quelle
4
Ihre Website scheint verschwunden zu sein. Hast du eine neue URL?
BrianFreud
1
Dies ist ein interessanter Ansatz. FWIW, ich erkenne Web Worker, indem ich prüfe, ob "self" (das globale Web Worker-Objekt) oder "window" vorhanden ist.
pwnall
Ich habe darüber nachdachte, wie PapaParse Griffe Web Worker und sie scheinen diesen Ansatz zu nehmen github.com/mholt/PapaParse
JP DeVries
Ich denke, das Testen mit 'typeof importScripts! == null' kann feststellen, ob das Skript im Worker-Bereich ausgeführt wird.
MeTTeO
1
Ich verstehe nicht, was das vorherige Geschwister aus dem Skriptelement ist. Kann mir jemand erklären?
Teemoh
28

Unter Verwendung des BlobVerfahrens, wie über dieses für einen Arbeiter Fabrik:

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'})));
}

Sie könnten es also so verwenden ...

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

BEARBEITEN:

Ich habe diese Idee gerade weiter ausgebaut, um die Thread -übergreifende Kommunikation zu vereinfachen: Bridged-Worker.js .

EDIT 2:

Der obige Link führt zu einem Kern, den ich erstellt habe. Jemand anderes verwandelte es später in ein echtes Repo .

Dan-Mann
quelle
11

Web-Worker arbeiten in völlig getrennten Kontexten als einzelne Programme.

Dies bedeutet, dass Code nicht in Objektform von einem Kontext in einen anderen verschoben werden kann, da sie dann über Schließungen, die zum anderen Kontext gehören, auf Objekte verweisen können.
Dies ist besonders wichtig, da ECMAScript als einzelne Thread-Sprache konzipiert ist. Da Web-Worker in separaten Threads arbeiten, besteht das Risiko, dass nicht thread-sichere Vorgänge ausgeführt werden.

Dies bedeutet wiederum, dass Web-Worker mit Code in Quellform initialisiert werden müssen.

Die Spezifikation von WHATWG sagt

Wenn der Ursprung der resultierenden absoluten URL nicht mit dem Ursprung des Eingabeskripts übereinstimmt, lösen Sie eine SECURITY_ERR-Ausnahme aus.

Daher müssen Skripte externe Dateien mit demselben Schema wie die Originalseite sein: Sie können ein Skript nicht von einer Daten: URL oder Javascript: URL laden, und eine https: -Seite konnte keine Mitarbeiter dazu bringen, Skripte mit http: URLs zu verwenden.

aber leider erklärt es nicht wirklich, warum man nicht zulassen konnte, dass eine Zeichenfolge mit Quellcode an den Konstruktor übergeben wurde.

Sean Kinsey
quelle
6

Ein besser lesbarer Weg für einen Inline-Mitarbeiter.

    var worker_fn = function(e) 
    {
        self.postMessage('msg from worker');            
    };

    var blob = new Blob(["onmessage ="+worker_fn.toString()], { type: "text/javascript" });

    var worker = new Worker(window.URL.createObjectURL(blob));
    worker.onmessage = function(e) 
    {
       alert(e.data);
    };
    worker.postMessage("start"); 
Chris Tobba
quelle
Was ich getan habe, war, dass ich eine Funktion mit dem gesamten Worker-Code erstellt, diese Funktion übergeben toString(), den Körper extrackiert und diese dann in einen Blob eingefügt habe. Überprüfen Sie bei der letzten Antwort, ich habe ein Beispiel
Fernando Carvajal
5

Nehmen Sie Adrias Antwort und fügen Sie sie in eine kopierbare Funktion ein, die mit aktuellem Chrome und FF, jedoch nicht mit IE10 funktioniert (Worker from Blob verursacht einen Sicherheitsfehler ).

var newWorker = function (funcObj) {
    // Build a worker from an anonymous function body
    var blobURL = URL.createObjectURL(new Blob(
        ['(', funcObj.toString(), ')()'],
        {type: 'application/javascript'}
     ));

    var worker = new Worker(blobURL);

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

    return worker;
}

Und hier ist ein funktionierendes Beispiel: http://jsfiddle.net/ubershmekel/YYzvr/

ubershmekel
quelle
5

Letzte Antwort (2018)

Sie können Greenlet verwenden :

Verschieben Sie eine asynchrone Funktion in einen 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'))
GG.
quelle
3

Abhängig von Ihrem Anwendungsfall können Sie so etwas wie verwenden

task.js Vereinfachte Schnittstelle zum Ausführen von CPU-intensivem Code auf allen Kernen (node.js und Web)

Ein Beispiel wäre

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});
Chad Scira
quelle
2

Schauen Sie sich das vkThread-Plugin an. Mit diesem Plugin können Sie jede Funktion in Ihrem Hauptcode übernehmen und in einem Thread (Web Worker) ausführen. Sie müssen also keine spezielle "Web-Worker-Datei" erstellen.

http://www.eslinstructor.net/vkthread/

- Vadim

vadimk
quelle
1

Sie können Web-Worker in demselben Javascript verwenden, indem Sie Inline-Webworker verwenden.

Der folgende Artikel befasst sich mit dem Verständnis der Webworker und ihrer Einschränkungen sowie dem Debuggen von Webworkern.

Mastering in Webworkern

kirankumar
quelle
1

Ich denke, der bessere Weg, dies zu tun, ist die Verwendung eines Blob-Objekts. Unten sehen Sie ein einfaches Beispiel.

// create a Blob object with a worker code
var blob = new Blob(["onmessage = function(e) { postMessage('msg from worker'); }"]);

// Obtain a blob URL reference to our worker 'file'.
var blobURL = window.URL.createObjectURL(blob);

// create a Worker
var worker = new Worker(blobURL);
worker.onmessage = function(e) {
  console.log(e.data);
};
worker.postMessage("Send some Data"); 
Miguel Q.
quelle
1

Versuchen Sie, jThread zu verwenden. https://github.com/cheprasov/jThread

// You can use simple calling like this
jThread(
    function(arr){
        //... some code for Worker
        return arr;
    }
    ,function(arr){
        //... done code
    }
)( [1,2,3,4,5,6,7] ); // some params
Alexander Cheprasov
quelle
1

hier Konsole:

var worker=new Worker(window.URL.createObjectURL(new Blob([function(){
  //Long-running work here
  postMessage('done');
}.toString().split('\n').slice(1,-1).join('\n')],{type:'text/javascript'})));

worker.addEventListener('message',function(event){
  console.log(event.data);
});
59naga
quelle
1

https://developer.mozilla.org/es/docs/Web/Guide/Performance/Using_web_workers

    // Syntax: asyncEval(code[, listener])

var asyncEval = (function () {

  var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D");

  oParser.onmessage = function (oEvent) {
    if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); }
    delete aListeners[oEvent.data.id];
  };


  return function (sCode, fListener) {
    aListeners.push(fListener || null);
    oParser.postMessage({
      "id": aListeners.length - 1,
      "code": sCode
    });
  };

})();
hamboy75
quelle
1

Verwenden Sie mein kleines Plugin https://github.com/zevero/worker-create

var worker_url = Worker.createURL(function(e){
  self.postMessage('Example post from Worker'); //your code here
});
var worker = new Worker(worker_url);
Zevero
quelle
1

Ich denke, wir haben jetzt dank der Vorlagenliterale in ES6 eine weitere coole Option dafür. Auf diese Weise können wir auf die zusätzliche Worker-Funktion (und ihren seltsamen Umfang) verzichten und einfach den Code, der für den Worker bestimmt ist, als mehrzeiligen Text schreiben, ähnlich wie in dem Fall, in dem wir Text gespeichert haben, ohne jedoch ein Dokument oder ein DOM zu benötigen um das in zu tun. Beispiel:

const workerScript = `
self.addEventListener('message', function(e) {
  var data = e.data;
  console.log('worker recieved: ',data);
  self.postMessage('worker added! :'+ addOne(data.value));
  self.close();//kills the worker
}, false);
`;

Hier ist ein Kern des Restes dieses Ansatzes .

Beachten Sie, dass wir alle zusätzlichen Funktionsabhängigkeiten, die wir möchten, in den Worker ziehen können, indem wir sie in einem Array sammeln und .toString auf jedem von ihnen ausführen, um sie ebenfalls in Zeichenfolgen zu reduzieren (sollte funktionieren, solange es sich um Funktionsdeklarationen handelt) und Stellen Sie das dann einfach der Skriptzeichenfolge voran. Auf diese Weise müssen wir keine Skripte importieren, die wir möglicherweise bereits in den Bereich des Codes gebündelt haben, den wir schreiben.

Der einzige wirkliche Nachteil dieser speziellen Version ist, dass Linters den Service-Worker-Code nicht fusseln können (da es sich nur um eine Zeichenfolge handelt), was ein Vorteil für den "separaten Worker-Funktionsansatz" ist.

Dtipson
quelle
1

Dies ist nur eine Ergänzung zu den oben genannten - ich habe eine schöne Vorlage zum Testen von Web-Workern in jsFiddle. Anstelle von Blob wird jsFiddles ?jsapi verwendet:

function workerFN() {
  self.onmessage = function(e) {
    switch(e.data.name) {
      case "" : 
      break;
      default:
        console.error("Unknown message:", e.data.name);
    }
  }
}
// This is a trick to generate real worker script that is loaded from server
var url = "/echo/js/?js="+encodeURIComponent("("+workerFN.toString()+")()");
var worker = new Worker(url);
worker.addEventListener("message", function(e) {
  switch(e.data.name) {
    case "" : 
    break;
    default:
      console.error("Unknown message:", e.data.name);
  }
})

Normale Web Worker- und Shared Worker- Vorlagen sind verfügbar.

Tomáš Zato - Monica wieder einsetzen
quelle
1

Ich habe festgestellt, dass CodePen derzeit keine Inline- <script>Tags mit Syntaxhervorhebung hervorhebt , die es nicht sind type="text/javascript"(oder die kein Typattribut haben).

Daher habe ich eine ähnliche, aber etwas andere Lösung mit beschrifteten Blöcken mit breakentwickelt. Dies ist die einzige Möglichkeit, ein <script>Tag zu verlassen, ohne eine Wrapper-Funktion zu erstellen (was nicht erforderlich ist).

<!DOCTYPE html>
<script id="worker1">
  worker: { // Labeled block wrapper

    if (typeof window === 'object') break worker; // Bail if we're not a 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>

msanford
quelle
1

Eine einfache versprochene Version, Function#callAsWorkerdie ein thisArg und Argumente (genau wie call) verwendet und ein Versprechen zurückgibt:

Function.prototype.callAsWorker = function (...args) {
    return new Promise( (resolve, reject) => {
        const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(...e.data));`,
            blob = new Blob([code], { type: "text/javascript" }),
            worker = new Worker(window.URL.createObjectURL(blob));
        worker.onmessage = e => (resolve(e.data), worker.terminate());
        worker.onerror = e => (reject(e.message), worker.terminate());
        worker.postMessage(args);
    });
}

// Demo
function add(...nums) {
    return nums.reduce( (a,b) => a+b );
}
// Let the worker execute the above function, with the specified arguments
add.callAsWorker(null, 1, 2, 3).then(function (result) {
    console.log('result: ', result);
});

Trincot
quelle
Sie sollten eine close()Methode hinzufügen , um Ihren Web-Worker-Life-Hook zu schließen. developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/…
Shahar ン ー ン Levi
@ Shahar ド ー ン Levi, die closeFunktion ist veraltet. Arbeitnehmer können jedoch gekündigt werden . Ich habe das jetzt hinzugefügt.
Trincot
0

Ich verwende Code wie diesen. Sie können Ihre Onmessage als eine andere Funktion als Nur-Text definieren, damit der Editor Ihren Code hervorheben kann und jshint funktioniert.

const worker = createWorker();

createWorker() {
    const scriptContent = getWorkerScript();
    const blob = new Blob([
        scriptContent,
    ], {
        type: "text/javascipt"
    });
    const worker = new Worker(window.URL.createObjectURL(blob));
    return worker;
}

getWorkerScript() {
    const script = {
        onmessage: function (e) {
            console.log(e);
            let result = "Hello " + e.data
            postMessage(result);
        }
    };
    let content = "";
    for (let prop in script){
        content += `${prop}=${script[prop].toString()}`;
    }
    return content;
}

Z.JC.
quelle
Schauen Sie sich meine Antwort an , ich habe das gerade getan, aber ich habe eine ganze Klasse geschrieben, um zu abstrahieren, wie man Rückrufe weiterleitet
Fernando Carvajal
0

Ja, es ist möglich, ich habe es mit Blob-Dateien gemacht und einen Rückruf weitergeleitet

Ich werde Ihnen zeigen, was eine Klasse, die ich geschrieben habe, tut und wie sie die Ausführung von Rückrufen im Hintergrund verwaltet.

Zuerst instanziieren Sie das GenericWebWorkermit den Daten, die Sie an den Rückruf übergeben Web Workermöchten, der in ausgeführt wird. Dazu gehören Funktionen, die Sie verwenden möchten, in diesem Fall eine Nummer, ein Datum und eine aufgerufene Funktionblocker

var worker = new GenericWebWorker(100, new Date(), blocker)

Diese Blockerfunktion führt für n Millisekunden eine unendliche Zeit aus

function blocker (ms) {
    var now = new Date().getTime();
    while(true) {
        if (new Date().getTime() > now +ms)
            return;
    }   
}

und dann benutzt du es so

worker.exec((num, date, fnBlocker) => {
    /*Everithing here does not block the main thread
      and this callback has access to the number, date and the blocker */
    fnBlocker(10000) //All of this run in backgrownd
    return num*10

}).then(d => console.log(d)) //Print 1000

Jetzt ist es Zeit, die Magie im folgenden Beispiel zu sehen

/*https://github.com/fercarvo/GenericWebWorker*/
class GenericWebWorker {
    constructor(...ags) {
        this.args = ags.map(a => (typeof a == 'function') ? {type:'fn', fn:a.toString()} : a)
    }

    async exec(cb) {
        var wk_string = this.worker.toString();
        wk_string = wk_string.substring(wk_string.indexOf('{') + 1, wk_string.lastIndexOf('}'));            
        var wk_link = window.URL.createObjectURL( new Blob([ wk_string ]) );
        var wk = new Worker(wk_link);

        wk.postMessage({ callback: cb.toString(), args: this.args });
 
        var resultado = await new Promise((next, error) => {
            wk.onmessage = e => (e.data && e.data.error) ? error(e.data.error) : next(e.data);
            wk.onerror = e => error(e.message);
        })

        wk.terminate(); window.URL.revokeObjectURL(wk_link);
        return resultado
    }

    async parallel(arr, cb) {
        var res = [...arr].map(it => new GenericWebWorker(it, ...this.args).exec(cb))
        var all = await Promise.all(res)
        return all
    }

    worker() {
        onmessage = async function (e) {
            try {                
                var cb = new Function(`return ${e.data.callback}`)();
                var args = e.data.args.map(p => (p.type == 'fn') ? new Function(`return ${p.fn}`)() : p);

                try {
                    var result = await cb.apply(this, args); //If it is a promise or async function
                    return postMessage(result)

                } catch (e) { throw new Error(`CallbackError: ${e}`) }
            } catch (e) { postMessage({error: e.message}) }
        }
    }
}


function blocker (ms) {
    var now = new Date().getTime();
    while(true) {
        if (new Date().getTime() > now +ms)
            return;
    }   
}

setInterval(()=> console.log("Not blocked " + Math.random()), 1000)

console.log("\n\nstarting blocking code in Worker\n\n")

var worker = new GenericWebWorker(100, new Date(), blocker)

worker.exec((num, date, fnBlocker) => {
    fnBlocker(7000) //All of this run in backgrownd
    return num*10    
})
.then(d => console.log(`\n\nEnd of blocking code: result ${d}\n\n`)) //Print 1000

Fernando Carvajal
quelle
0

Sie können den Inhalt Ihrer Datei worker.js in Backticks platzieren (was eine mehrzeilige Zeichenfolgenkonstante ermöglicht) und den Worker aus einem Blob wie folgt erstellen:

var workerScript = `
    self.onmessage = function(e) {
        self.postMessage('message from worker');
    };
    // rest of worker code goes here
`;

var worker =
    new Worker(createObjectURL(new Blob([workerScript], { type: "text/javascript" })));

Dies ist praktisch, wenn Sie aus irgendeinem Grund keine separaten Skript-Tags für den Worker haben möchten.

Samgak
quelle
0

Eine andere Lösung besteht darin, den Worker in eine Funktion zu verpacken und dann einen Blob zu erstellen, der die Funktion wie folgt aufruft:

     function workerCode() {
        self.onmessage = function (e) {
          console.log("Got message from parent", e.data);
        };
        setTimeout(() => {
          self.postMessage("Message From Worker");
        }, 2000);
      }

      let blob = new Blob([
        "(" + workerCode.toString() + ")()"
      ], {type: "text/javascript"});

      // Note: window.webkitURL.createObjectURL() in Chrome 10+.
      let worker = new Worker(window.URL.createObjectURL(blob));
      worker.onmessage = function (e) {
        console.log("Received: " + e.data);
      };
      worker.postMessage("hello"); // Start the worker.
Shlomi Schwartz
quelle
-1

Einzeiler für die Ausführung von Funktionen bei Arbeitnehmern:

const FunctionalWorker = fn => new Worker(window.URL.createObjectURL(new Blob(["(" + workerCode.toString() + ")()"], {type: "text/javascript"})));

Anwendungsbeispiel:

let fn = FunctionalWorker(() => {
    self.postMessage("hi");
});
fn.onmessage = msg => {
    console.log(msg);
};
ifeelbadformyoldquestions
quelle