据我所知,web worker需要写在一个单独的JavaScript文件中,并像这样调用:

new Worker('longrunning.js')

我正在使用闭包编译器来合并和缩小我所有的JavaScript源代码,我不希望将我的worker放在单独的文件中进行分发。有什么办法可以做到吗?

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

既然一级函数对JavaScript如此重要,为什么标准的后台工作方式必须从web服务器加载整个其他JavaScript文件呢?


当前回答

你可以把worker.js文件的内容放在反勾号(允许多行字符串常量)中,然后像这样创建一个blob:

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

如果出于某种原因,您不想为worker使用单独的脚本标记,那么这是非常方便的。

其他回答

您可以创建一个JavaScript文件,该文件可以感知其执行上下文,并且可以充当父脚本和工作脚本。让我们从这样一个文件的基本结构开始:

(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);

如您所见,脚本包含父进程和工作者进程的所有代码,检查它自己的实例是否是一个具有!document的工作者进程。有点笨拙的script_path计算用于精确计算相对于父页的脚本路径,因为提供给新Worker的路径是相对于父页的,而不是相对于脚本的。

看一下vkThread插件。使用htis插件,你可以在主代码中获取任何函数,并在线程(web worker)中执行它。所以,你不需要创建一个特殊的“web-worker文件”。

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

——瓦迪姆

所以我认为我们现在有了另一个很酷的选择,这要感谢ES6中的模板文字。这允许我们省去额外的worker函数(及其奇怪的作用域),只需将用于worker的代码编写为多行文本,就像我们用来存储文本的情况一样,但实际上不需要使用文档或DOM来实现。例子:

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);
`;

下面是该方法的其余要点。

请注意,我们可以将任何我们想要的额外函数依赖项拉入worker,只需将它们收集到一个数组中,并对每个函数运行. tostring,将它们简化为字符串(只要它们是函数声明就可以工作),然后将其添加到脚本字符串中。这样,我们就不必导入我们可能已经捆绑到正在编写的代码范围中的脚本。

这个特定版本唯一的缺点是lints无法检测service worker代码(因为它只是一个字符串),这对于“单独的worker函数方法”来说是一个优势。

有一些答案,但这里是另一个内联版本。

注意:“self”参数纯粹是为了检测目的而做的修饰,实际的工作代码从第一个大括号开始,self是正常的

inlineWorker ( "hello world",// initial message to send to worker function(self){ // inline worker code. self.onmessage = function (e) { self.postMessage("thinking..."); for (var i=0;i<100000000;i++) { var r = Math.random(); } self.postMessage(e.data.toUpperCase()); } },function(e){ // optional message handler document.getElementById("log").innerHTML= "from worker:"+e.data; }); function inlineWorker (msg,fn,onMsg) { var w=window, U=!!w.webkitURL?w.webkitURL:w.URL, src=fn.toString(), s=src.indexOf('{'), e=src.lastIndexOf('}'), worker = new Worker(U.createObjectURL( new Blob([ src.substring(s+1,e-1) ], { type: "text/javascript" }) )); if (typeof onMsg==="function") { worker.addEventListener("message",onMsg); } if (msg) { worker.postMessage(msg); } return worker; } <div id="log"></div>

对于Node.js实现,可以使用以下对Trincot答案的改编。再次注意,Function.prototype.callAsWorker()接受一个thisArg和参数,就像Function.prototype.call()一样,并返回一个承诺。

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