据我所知,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"); 

其他回答

是的,这是可能的,我做到了使用Blob文件和传递回调

我将向您展示我编写的一个类是做什么的,以及它如何在后台管理回调的执行。

首先,你要实例化GenericWebWorker,使用你想要传递给回调的任何数据,这些数据将在WebWorker中执行,包括你想要使用的函数,在这种情况下,一个数字,一个日期和一个叫做blocker的函数

var worker = new GenericWebWorker(100, new Date(), blocker)

这个阻塞器函数将在n毫秒内执行一个无限while

function blocker (ms) {
    var now = new Date().getTime();
    while(true) {
        if (new Date().getTime() > now +ms)
            return;
    }   
}

然后像这样用

worker.exec((num, date, fnBlocker) => {
    /*Everithing here does not block the main thread
      and this callback has access to the number, date and the blocker */
    fnBlocker(10000) //All of this run in backgrownd
    return num*10

}).then(d => console.log(d)) //Print 1000

现在,是时候看看下面例子中的神奇之处了

/*https://github.com/fercarvo/GenericWebWorker*/ class GenericWebWorker { constructor(...ags) { this.args = ags.map(a => (typeof a == 'function') ? {type:'fn', fn:a.toString()} : a) } async exec(cb) { var wk_string = this.worker.toString(); wk_string = wk_string.substring(wk_string.indexOf('{') + 1, wk_string.lastIndexOf('}')); var wk_link = window.URL.createObjectURL( new Blob([ wk_string ]) ); var wk = new Worker(wk_link); wk.postMessage({ callback: cb.toString(), args: this.args }); var resultado = await new Promise((next, error) => { wk.onmessage = e => (e.data && e.data.error) ? error(e.data.error) : next(e.data); wk.onerror = e => error(e.message); }) wk.terminate(); window.URL.revokeObjectURL(wk_link); return resultado } async parallel(arr, cb) { var res = [...arr].map(it => new GenericWebWorker(it, ...this.args).exec(cb)) var all = await Promise.all(res) return all } worker() { onmessage = async function (e) { try { var cb = new Function(`return ${e.data.callback}`)(); var args = e.data.args.map(p => (p.type == 'fn') ? new Function(`return ${p.fn}`)() : p); try { var result = await cb.apply(this, args); //If it is a promise or async function return postMessage(result) } catch (e) { throw new Error(`CallbackError: ${e}`) } } catch (e) { postMessage({error: e.message}) } } } } function blocker (ms) { var now = new Date().getTime(); while(true) { if (new Date().getTime() > now +ms) return; } } setInterval(()=> console.log("Not blocked " + Math.random()), 1000) console.log("\n\nstarting blocking code in Worker\n\n") var worker = new GenericWebWorker(100, new Date(), blocker) worker.exec((num, date, fnBlocker) => { fnBlocker(7000) //All of this run in backgrownd return num*10 }) .then(d => console.log(`\n\nEnd of blocking code: result ${d}\n\n`)) //Print 1000

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

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

——瓦迪姆

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

近期答案(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'))

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>