为什么Web Worker不能直接调用函数?

时间:2012-07-06 02:12:14

标签: html5 web-worker

我们可以像这样使用HTML5中的web worker:

var worker = new Worker('worker.js');

但为什么我们不能调用这样的函数?

var worker = new Worker(function(){
    //do something
});

7 个答案:

答案 0 :(得分:18)

这是网络工作者的设计方式。它们必须有自己的外部JS文件和由该文件初始化的自己的环境。出于多线程冲突的原因,他们无法与常规的全局JS空间共享环境。

不允许Web工作者直接访问全局变量的一个原因是,它需要两个环境之间的线程同步,这不是可用的东西(并且会使事情变得非常复杂)。当web worker有自己独立的全局变量时,除了通过与主JS线程正确同步的消息队列之外,他们不能搞乱主JS线程。

也许有一天,更高级的JS程序员将能够使用传统的线程同步技术来共享对公共变量的访问,但是现在两个线程之间的所有通信都必须通过消息队列,并且web worker无法访问主要的Javascript线程的环境。

答案 1 :(得分:10)

此问题已被问及before,但出于某种原因,OP决定将其删除。
我重新发布我的答案,以防需要一个方法来从函数创建Web worker。


this post中,显示了三种从任意字符串创建Web worker的方法。在这个答案中,我使用的是第三种方法,因为它在所有环境中都受支持。

需要帮助文件:

// Worker-helper.js
self.onmessage = function(e) {
    self.onmessage = null; // Clean-up
    eval(e.data);
};

在您的实际Worker中,此帮助程序文件的用法如下:

// Create a Web Worker from a function, which fully runs in the scope of a new
//    Worker
function spawnWorker(func) {
    // Stringify the code. Example:  (function(){/*logic*/}).call(self);
    var code = '(' + func + ').call(self);';
    var worker = new Worker('Worker-helper.js');
    // Initialise worker
    worker.postMessage(code);
    return worker;
}

var worker = spawnWorker(function() {
    // This function runs in the context of a separate Worker
    self.onmessage = function(e) {
        // Example: Throw any messages back
        self.postMessage(e.data);
    };
    // etc..
});
worker.onmessage = function() {
    // logic ...
};
worker.postMessage('Example');

请注意,范围是严格分开的。变量只能使用worker.postMessageworker.onmessage来回传递。所有邮件均为structured clones

答案 2 :(得分:3)

这个答案可能有点晚了,但我写了一个库来简化网络工作者的使用,它可能适合OP的需要。看看:https://github.com/derekchiang/simple-worker

它允许您执行以下操作:

SimpleWorker.run({
  func: intensiveFunction,
  args: [123456],
  success: function(res) {
    // do whatever you want
  },
  error: function(err) {
    // do whatever you want
  }
})

答案 3 :(得分:1)

虽然它不是最优的并且在评论中已经提到过,但如果您的浏览器支持Web Worker的blobURL,则不需要外部文件。 HTML5Rocks是我代码的inspiration

function sample(e)
{
    postMessage(sample_dependency());
}

function sample_dependency()
{
    return "BlobURLs rock!";
}

var blob = new Blob(["onmessage = " + sample + "\n" + sample_dependency]);
var blobURL = window.URL.createObjectURL(blob);
var worker = new Worker(blobURL);

worker.onmessage = function(e)
{
    console.log(e.data);
};

worker.postMessage("");

注意事项:

  • blob worker不会成功使用相对URL。 HTML5Rocks链接涵盖了这一点,但它不是原始问题的一部分。

  • 人们报告了使用Blob URL与Web Workers的问题。我已经尝试过使用IE11(FCU附带的任何内容),MS Edge 41.16299(Fall Creator的更新),Firefox 57和Chrome 62.没有关于Safari支持的线索。我测试的那些已经有效了。

  • 请注意,Blob构造函数调用中的“sample”和“sample_dependency”引用会隐式调用Function.prototype.toString()作为sample.toString()sample_dependency.toString(),这与调用{{1}非常不同}和toString(sample)

发布这个是因为它是第一个在搜索如何使用Web Workers而不请求其他文件时出现的stackoverflow。

看看Zevero的答案,他的回购中的代码看似相似。如果您更喜欢干净的包装器,那么这与他的代码大致相同。

最后 - 我在这里是一个菜鸟,所以任何/所有更正都会受到赞赏。

答案 4 :(得分:0)

通过设计Web工作者是多线程的,javascript是单线程的“ *”多个脚本不能同时运行。

请参阅:http://www.html5rocks.com/en/tutorials/workers/basics/

答案 5 :(得分:0)

只需使用我的小插件https://github.com/zevero/worker-create

并做

var worker_url = Worker.create(function(e){
  self.postMessage('Example post from Worker'); //your code here
});
var worker = new Worker(worker_url);

答案 6 :(得分:0)

WebWorkers Essentials

WebWorkers在一个独立的线程中执行,因此对主线程进行无访问,在那里声明它们(反之亦然)。由此产生的范围是孤立的,并受到限制。这就是为什么,例如,您无法从工作人员中获取DOM。

与WebWorkers的沟通

因为线程之间的通信是必要的,所以有机制来实现它。标准通信机制是通过消息,使用 worker.postMessage()函数和 worker.onMessage(),事件处理程序。

有更多高级技术可用,涉及sharedArrayBuffers,但我的目标不是覆盖它们。如果您对它们感兴趣,请阅读here

线程函数

标准带给我们的是什么。 但是,ES6为我们提供了足够的工具来实现on-demmand可调用的 Threaded-Function

由于您可以从 Blob 构建Worker,并且您的Function可以转换为它(使用 URL.createObjectURL ),您只需要实现某种类型的两个线程中的通信层,为您处理消息,并获得自然的交互。

承诺当然是你的朋友,考虑到一切都会发生异步

应用这一理论,您可以实现easilly,即您描述的场景。

我的个人方法:ParallelFunction

我最近实施并发布了一个小型图书馆,它完全符合您的描述。小于2KB(缩小)。

它被称为 ParallelFunction ,它可以在githubnpm和几个CDNs中使用。

如您所见,它完全符合您的要求:

// Your function...
let calculatePi = new ParallelFunction( function(n){
    // n determines the precision , and in consequence 
    // the computing time to complete      
    var v = 0;
    for(let i=1; i<=n; i+=4) v += ( 1/i ) - ( 1/(i+2) );
    return 4*v;
});

// Your async call...
calculatePi(1000000).then( r=> console.log(r) );

// if you are inside an async function you can use await...
( async function(){    
    let result = await calculatePi(1000000);
    console.log( result );
})()

// once you are done with it...
calculatePi.destroy();

初始化后,您可以根据需要多次调用您的函数。当函数完成执行时,将返回 Promise ,将解决。

顺便说一句,许多其他的图书馆都存在。