Web工作者没有一个单独的JavaScript文件?

据我所知,networking工作者需要写在一个单独的JavaScript文件中,像这样调用:

new Worker('longrunning.js') 

我正在使用闭包编译器来组合和缩小所有的JavaScript源代码,我宁愿不必让我的工作人员在单独的文件分发。 有没有办法做到这一点?

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

鉴于一stream的function对于JavaScript来说非常重要,为什么做后台工作的标准方式必须从服务器加载一个完整的JavaScript文件呢?

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

如果你想立即创build你的工作脚本,或者创build一个独立的页面,而不必创build单独的工作文件呢? 使用Blob(),您可以通过创build一个工作者代码的URL句柄作为一个string来“embedded”您的工作者在与主逻辑相同的HTML文件中

BLOB内联工作者的完整示例:

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

将web worker代码embedded到HTML中的html5rocks解决scheme相当糟糕。
另外一个逃逸的JavaScript-as-a-string是不行的,因为它使得工作stream变得复杂(Closure编译器不能在string上运行)。

我个人真的很喜欢toString方法,但@ dan-man这个正则expression式!

我的首选方法是:

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

支持是这三个表的交集:

但是,这对SharedWorker不起作用,因为即使可选的“name”参数匹配,URL也必须完全匹配。 对于SharedWorker,您将需要一个单独的JavaScript文件。


2015更新 – ServiceWorker奇点到达

现在解决这个问题还有更强大的方法。 再次,将工作代码存储为函数(而不是静态string)并使用.toString()进行转换,然后将代码插入到您select的静态URL下的CacheStorage中。

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

有两个可能的倒退。 像上面的ObjectURL,或者更加无缝的,把一个真正的 JavaScript文件放到/my_workers/worker1.js

这种方法的优点是:

  1. SharedWorkers也可以被支持。
  2. 标签可以共享一个固定地址的单个caching副本。 blob方法为每个选项卡增加了随机的objectURL。

您可以创build一个能够识别其执行上下文的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计算用于精确计算相对于父页面的脚本path,因为提供给new Worker的path相对于父页面而不是脚本。

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

编辑:

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

编辑2:

上面的链接是我创build的要点。 其他人后来把它变成了一个实际的回购 。

networking工作人员与个人计划完全分离。

这意味着代码不能以对象forms从一个上下文移动到另一个上下文,因为他们将能够通过属于另一个上下文的闭包来引用对象。
由于ECMAScript被devise成单线程语言,因此这一点尤其重要,而且由于Web工作人员在不同的线程中运行,您将会面临执行非线程安全操作的风险。

这也意味着networking工作者需要以源代码的forms初始化代码。

WHATWG的规格说

如果生成的绝对URL的来源与条目脚本的来源不同,则会抛出SECURITY_ERRexception。

因此,脚本必须是与原始页面具有相同scheme的外部文件:无法从data:URL或javascript:URL加载脚本,并且https:页面无法使用带有http:URL的脚本启动worker。

但不幸的是,它并没有真正解释为什么不能允许将带有源代码的string传递给构造函数。

一个更好的阅读方式为内联工作者..

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

采取亚德里亚的反应,并把它放在一个可复制的function,与当前的Chrome和FF,但不是IE10(blob工人导致安全错误 )的作品。

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

这里有一个工作的例子http://jsfiddle.net/ubershmekel/YYzvr/

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

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

–Vadim

根据你的用例,你可以使用类似的东西

task.js简化了CPU密集代码在所有内核上运行的接口(node.js和web)

一个例子是

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

您可以使用内嵌web工作人员使用相同的JavaScript网页工作。

下面的文章将解决您轻松了解networking工作者和他们的限制和webworkersdebugging。

掌握在webworkers

尝试使用jThread。 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 

这里控制台:

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

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

使用我的小插件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); 

所以我想我们现在有另一个很酷的select,这要归功于ES6中的模板文字。 这使我们可以省去额外的工作者函数(及其奇怪的范围),只是将用于工作者的代码作为多行文本写出来,就像我们用来存储文本的情况一样,但实际上不需要文档或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以将它们还原为string(只要它们是函数声明即可)那么只需将其添加到脚本string即可。 这样我们就不必importScript,我们可能已经绑定到我们正在编写的代码的范围。

这个特定版本唯一的缺点是,linters将不能删除服务工作者代码(因为它只是一个string),这对于“单独的工作者函数方法”是个好处。

这只是上面的一个补充 – 我有一个很好的模板来testingweb工作人员在jsFiddle。 而不是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); } }) 

普通networking工作者和共享工作者模板是可用的。

我发现CodePen目前没有type="text/javascript" (或者没有type属性)的语法高亮内联<script>标签。

所以我devise了一个类似但略有不同的解决scheme,使用带有break 标签块 ,这是您可以从<script>标签保释而不用创build包装函数(这是不必要的)的唯一方法。

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

我认为更好的方法是使用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"); 

我使用这样的代码,你可以定义你的onmessage作为纯文本以外的function,所以编辑器可以突出显示你的代码和jshint的作品。

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