据我所知,web worker需要写在一个单独的JavaScript文件中,并像这样调用:
new Worker('longrunning.js')
我正在使用闭包编译器来合并和缩小我所有的JavaScript源代码,我不希望将我的worker放在单独的文件中进行分发。有什么办法可以做到吗?
new Worker(function() {
//Long-running work here
});
既然一级函数对JavaScript如此重要,为什么标准的后台工作方式必须从web服务器加载整个其他JavaScript文件呢?
我的看法是:
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)
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
});
};
})();
我发现CodePen目前不语法高亮内联<script>标签,不是type="text/javascript"(或没有类型属性)。
因此,我设计了一个类似但略有不同的解决方案,使用带break的标记块,这是摆脱<script>标记而不创建包装器函数(这是不必要的)的唯一方法。
<!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>
一个简单的承诺版本,Function#callAsWorker,它接受一个thisArg和参数(就像call一样),并返回一个承诺:
Function.prototype.callAsWorker = function (...args) {
return new Promise( (resolve, reject) => {
const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(...e.data));`,
blob = new Blob([code], { type: "text/javascript" }),
worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = e => (resolve(e.data), worker.terminate());
worker.onerror = e => (reject(e.message), worker.terminate());
worker.postMessage(args);
});
}
// Demo
function add(...nums) {
return nums.reduce( (a,b) => a+b );
}
// Let the worker execute the above function, with the specified arguments
add.callAsWorker(null, 1, 2, 3).then(function (result) {
console.log('result: ', result);
});