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

new Worker('longrunning.js')

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

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

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


当前回答

这是一个有点离题的答案,但是……您可能不需要使用网络工作者来处理浏览器上的长时间工作。

让我们假设你想要运行几次繁重的计算(就像你对数组做的那样):

const heavyFct = () => {let i = 0; while(i<1e8) {++i}}

for (let i = 0; i < 100; ++i) {
  heavyFct();
}

这将冻结您的浏览器。

为了避免这种情况,我们可以这样依赖setTimeout:

const desync = (i = 0) => {
  if (i >= 100) {return}
  heavyFct();
  setTimeout(() => desync(i + 1), 0);
}
desync();

现在,您可以在不冻结计算机的情况下运行繁重的计算

其他回答

我喜欢ifbamoq给出的答案,但由于堆栈溢出的积分政策,我无法评论。因此,我将给出一个示例,展示一些正在进行的密集工作——以及它如何不锁定主线程。

如果你像我一样双击html文件,把它们当成小程序,就不会遇到空原点的CORS问题。: -)

<!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>

控制台:

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

使用Blob方法,对于工人工厂来说是这样的:

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

所以你可以这样使用它…

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

编辑:

我只是进一步扩展了这个想法,以便更容易地进行跨线程通信:bridge -worker.js。

编辑2:

上面的链接是我创建的一个要点。后来又有人把它变成了真正的回购。

@Trincot's似乎是目前为止最好的。然而,也许我们可以进一步发展它。所以我的想法是,

我们不要修改Function.prototype。 为线程操作获取函数的承诺/线程化版本。 如果需要,请确保仍然可以同步调用该函数。

因此,我们用spawn方法定义了Threadable类。一旦我们将函数设为该类的成员,那么它就是可线程的:)

class Threadable extends Function { constructor(f){ super("...as",`return ${f.toString()}.apply(this,as)`); } spawn(...as){ var code = `self.onmessage = m => self.postMessage((${this.toString()}).apply(self,m.data));`, blob = new Blob([code], {type: "text/javascript"}), wrkr = new Worker(window.URL.createObjectURL(blob)); return new Promise( (v,x) => ( wrkr.onmessage = m => (v(m.data), wrkr.terminate()) , wrkr.onerror = e => (x(e.message), wrkr.terminate()) , wrkr.postMessage(as) ) ); } } function add(...nums) { return nums.reduce((a,b) => a+b); } var addT = new Threadable(add); addT.spawn(1,2,3,4) .then(m => console.log(`Promisified thread returned ${m}`)); console.log(`Synchronous invocation of addT returned ${addT(1,2,3,4)}`);

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

如果您希望动态地创建工作人员脚本,或者创建一个自包含的页面,而不必创建单独的工作人员文件,该怎么办?使用Blob(),您可以通过将工作代码的URL句柄创建为字符串,将工作代码“内联”到与主逻辑相同的HTML文件中


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>