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

new Worker('longrunning.js')

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

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

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


当前回答

我认为更好的方法是使用Blob对象,下面你可以看到一个简单的例子。

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

其他回答

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

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

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

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

控制台:

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

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

这只是上面的一个补充-我在jsFiddle中有一个很好的模板用于测试web worker。而不是Blob,它使用jsFiddles ?js api:

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

有普通的web worker模板和共享worker模板。

近期答案(2018)

你可以使用Greenlet:

移动一个异步函数到它自己的线程中。Workerize的简化单功能版本。

例子:

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