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.

3voto

Dtipson Punkte 1475

Ich denke, dass wir dank der Template-Literale in ES6 jetzt eine weitere coole Option für diese Aufgabe haben. Damit können wir auf die zusätzliche Worker-Funktion (und ihren seltsamen Anwendungsbereich) 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 speichern, aber ohne tatsächlich ein Dokument oder DOM zu benötigen, um das 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 der Rest des Ansatzes im Wesentlichen .

Beachten Sie, dass wir alle zusätzlichen Funktionsabhängigkeiten in den Worker ziehen können, indem wir sie in einem Array sammeln und .toString auf jeden von ihnen anwenden, um sie ebenfalls in Strings zu reduzieren (sollte funktionieren, solange es sich um Funktionsdeklarationen handelt), und dies dann einfach dem Skriptstring voranstellen. Auf diese Weise müssen wir keine Skripte importieren, die wir möglicherweise bereits in den Bereich des Codes, den wir schreiben, gebündelt haben.

Der einzige wirkliche Nachteil dieser speziellen Version ist, dass Linters nicht in der Lage sind, den Code des Service Workers zu linsen (da es sich nur um eine Zeichenkette handelt), was ein Vorteil für den "separaten Worker-Funktionsansatz" ist.

2voto

vadimk Punkte 1455

Werfen Sie einen Blick auf das vkThread-Plugin. Mit diesem Plugin können Sie jede Funktion in Ihrem Hauptcode in einem Thread (Webworker) ausführen. Sie brauchen also keine spezielle "Web-Worker-Datei" zu erstellen.

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

--Vadim

1voto

maxone44 Punkte 1

Ich fand die Antwort von ifbamoq gut, konnte aber wegen der Punktepolitik von Stack Overflow keinen Kommentar abgeben. Daher werde ich ein Beispiel geben, das zeigt, wie intensive Arbeit geleistet wird - und wie der Hauptthread nicht gesperrt wird.

Und das alles, ohne CORS-Probleme mit dem Null-Ursprung zu bekommen - wenn Sie wie ich sind und gerne auf die HTML-Dateien doppelklicken und sie wie kleine Programme behandeln :-)

<!DOCTYPE html>
<html>
    <head>
        <title>Worker example: One-core computation</title>
    </head>
    <body>
        <p>The highest prime number discovered so far is: <div id="result"></div></p>
    </body>

    <script>
        // let worker = new Worker('WebWorker.js');  // lets skip this to avoid null origin issues

        let WorkerFn = (event) =>
        {
            let isPrime = false;

            for (let n = 2; n <= 1_000_000; n++)
            {
                isPrime = true;

                for(let i = 2; i <= Math.sqrt(n); i++)
                    if (n % i == 0)
                        isPrime = false;   // If you can get thru all this shit and survive, ur prime!

                if (isPrime)
                    postMessage(n);
            }
        }

        let worker = new Worker(window.URL.createObjectURL(new Blob(["(" + WorkerFn.toString() + ")()"], {type: "text/javascript"})));

        worker.onmessage = (event) =>
        {
            result.innerHTML = event.data;
        }

    </script>
</html>

1voto

Trentium Punkte 2885

Für eine Node.js-Implementierung ist die folgende Anpassung von Trincot's Antwort eingesetzt werden können. Beachten Sie erneut, dass Function.prototype.callAsWorker() nimmt eine thisArg und Argumente, genau wie Function.prototype.call() und gibt ein Versprechen zurück.

const { Worker } = require ( 'worker_threads' );

Function.prototype.callAsWorker = function ( ...args ) {
    return new Promise( ( resolve, reject ) => {

        const code = `
            const { parentPort, workerData } = require ( 'worker_threads' );
            parentPort.postMessage( ( ${this.toString()} ).call( ...workerData ) )
        `;
        const worker = new Worker( code, { eval: true, workerData: args } );

        worker.on('message', ( msg ) => { resolve( msg ), worker.terminate() } );
        worker.on('error', ( err ) => { reject( err ), worker.terminate() } );
        worker.on('exit', ( code ) => {
            if ( code !== 0 ) {
                reject( new Error( `Worker stopped with exit code ${code}.` ) );
            }
        });

    });
}

// Demo
function add( ...nums ) {
    return nums.reduce( ( a, b ) => a + b );
}

// Let the worker execute the above function, with the specified arguments
let result = await add.callAsWorker( null, 1, 2, 3 );
console.log( 'result: ', result );

1voto

Shimon Doodkin Punkte 3946

Meine Meinung dazu:

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

function createAsyncWorker(fn){

    // asyncworker=createAsyncWorker(function(){
    //     importScripts('my_otherscript.js');
    //     self.onmessage = function([arg1,arg2]) {
    //         self.postMessage('msg from worker');
    //     };
    // })
    // await asyncworker.postMessage('arg1','value')
    // await asyncworker.postMessage('arg1','value')
    // asyncworker.worker.terminate()

    var worker = BuildWorker(fn);

    function postMessage(...message){
        let external={}, promise= new Promise((resolve,reject)=>{external.resolve=resolve;external.reject=reject;})
        worker.onmessage = function(message){ external.resolve(message.data)};
        worker.postMessage(message); // Start the worker.
        return promise;
    }

    return {worker,postMessage};
}

Anwendungsbeispiel:

autoarima = createAsyncWorker(function(){
    importScripts("https://127.0.0.1:11000/arima.js")

    self.onmessage=(message)=>{
        let [action,arg1,arg2]=message.data
        if(action=='load')
        {
            ARIMAPromise.then(ARIMA1 => {
                ARIMA=ARIMA1
                autoarima = new ARIMA({ auto: true });
                //   const ts = Array(10).fill(0).map((_, i) => i + Math.random() / 5)
                //   const arima = new ARIMA({ p: 2, d: 1, q: 2, P: 0, D: 0, Q: 0, S: 0, verbose: false }).train(ts)
                //   const [pred, errors] = arima.predict(10)
                postMessage('ok')
            });
        }
        if(action=='fit')
        {
            autoarima.fit(arg1)
            postMessage('ok')
        }
        if(action=='predict')
        {
            postMessage(autoarima.predict(arg1,arg2)) 
        }
    };
})
autoarima.terminate=function(){  this.worker.terminate(); }
autoarima.load=async function(...args){return await this.postMessage('load',...args)}
autoarima.fit=async function(...args){return await this.postMessage('fit',...args)}
autoarima.predict=async function(...args){return await this.postMessage('predict',...args)}

await autoarima.load()
await autoarima.fit(b_values)
await autoarima.predict(1)

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