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

new Worker('longrunning.js')

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

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

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


当前回答

一个简单的承诺版本,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); });

其他回答

我的看法是:

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)

使用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:

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

在worker中运行函数的一行代码:

const FunctionalWorker = fn => new Worker(window.URL.createObjectURL(new Blob(["(" + fn.toString() + ")()"], {type: "text/javascript"})));

使用示例:

let fn = FunctionalWorker(() => {
    self.postMessage("hi");
});
fn.onmessage = msg => {
    console.log(msg);
};

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

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