据我所知,web worker需要写在一个单独的JavaScript文件中,并像这样调用:
new Worker('longrunning.js')
我正在使用闭包编译器来合并和缩小我所有的JavaScript源代码,我不希望将我的worker放在单独的文件中进行分发。有什么办法可以做到吗?
new Worker(function() {
//Long-running work here
});
既然一级函数对JavaScript如此重要,为什么标准的后台工作方式必须从web服务器加载整个其他JavaScript文件呢?
我喜欢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>
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>
所以我认为我们现在有了另一个很酷的选择,这要感谢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函数方法”来说是一个优势。
我的看法是:
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};
}
使用的例子:
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)