web-dev-qa-db-ja.com

Web Workerが関数を直接呼び出せないのはなぜですか?

次のように、HTML5でWebワーカーを使用できます。

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

しかし、なぜこのような関数を呼び出せないのでしょうか。

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

これがWebワーカーの設計方法です。独自の外部JSファイルと、そのファイルによって初期化された独自の環境が必要です。マルチスレッドの競合のため、通常のグローバルJSスペースと環境を共有することはできません。

Webワーカーがグローバル変数への直接アクセスを許可されない理由の1つは、2つの環境間でスレッドの同期が必要になるためです。これは、利用できるものではありません(そして、事態を深刻に複雑にします)。 Webワーカーが独自の個別のグローバル変数を持っている場合、メインJSスレッドと適切に同期されているメッセージングキューを経由する場合を除いて、メインJSスレッドを混乱させることはできません。

おそらくいつか、より高度なJSプログラマーは、従来のスレッド同期技術を使用して共通変数へのアクセスを共有できるようになりますが、今のところ、2つのスレッド間のすべての通信はメッセージキューを経由する必要があり、WebワーカーはメインのJavascriptスレッドにアクセスできません。環境。

19
jfriend00

この質問は に尋ねられましたが、何らかの理由で、OPはそれを削除することを決定しました。
関数からWebワーカーを作成するメソッドが必要な場合に備えて、回答を再投稿します。


この投稿 では、任意の文字列からWebワーカーを作成する3つの方法が示されました。この回答では、すべての環境でサポートされているため、3番目の方法を使用しています。

ヘルパーファイルが必要です:

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

実際のワーカーでは、このヘルパーファイルは次のように使用されます。

// 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.postMessageおよびworker.onmessageを使用してのみ受け渡しできます。すべてのメッセージは 構造化クローン です。

10
Rob W

この答えは少し遅れるかもしれませんが、私はWebワーカーの使用を簡素化するためにライブラリを作成し、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
Derek Chiang

これは最適ではなく、コメントで言及されていますが、ブラウザーがWebワーカーのblobURLをサポートしている場合は、外部ファイルは必要ありません。 HTML5Rocksは私のコードの インスピレーション でした:

_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("");
_

警告:

  • ブロブワーカーは相対URLを正常に使用しません。 HTML5Rocksリンクはこれをカバーしていますが、元の質問の一部ではありませんでした。

  • 人々は、WebワーカーでBlobURLを使用する際の問題を報告しています。 IE11(FCUに同梱されているものは何でも)、MS Edge 41.16299(Fall Creator's Update)、Firefox 57、およびChrome62。Safariのサポートに関する手がかりはありません。私が持っているものです。テストは機能しました。

  • Blobコンストラクター呼び出しの「sample」および「sample_dependency」参照は、暗黙的にFunction.prototype.toString()sample.toString()およびsample_dependency.toString()として呼び出すことに注意してください。これは、toString(sample)およびtoString(sample_dependency)

これは、追加のファイルを要求せずにWebワーカーを使用する方法を検索したときに発生した最初のスタックオーバーフローであるため、これを投稿しました。

Zeveroの回答を見てみると、彼のリポジトリのコードは似ているように見えます。クリーンなラッパーを好む場合、これはほぼ彼のコードが行うことです。

最後に-私はここでは初心者なので、すべての修正を歓迎します。

1
lmcdo

私の小さなプラグインを使用するだけです 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);
0
zevero

設計上、Webワーカーはマルチスレッドであり、JavaScriptはシングルスレッドです "*"複数のスクリプトを同時に実行することはできません。

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

0
Shawn E Carter

WebWorkers Essentials

WebWorkersは独立したスレッドで実行されるため、アクセスなしそれらを宣言するメインスレッド(およびその逆)。結果のスコープは分離され、制限されます。そのため、たとえば、ワーカー内からDOMにアクセスすることはできません。


WebWorkersとのコミュニケーション

スレッド間の通信が必要なため、それを実現するメカニズムがあります。標準の通信メカニズムは、worker.postMessage()関数とworker.onMessage()を使用したメッセージを介したものです。 、イベントハンドラー。

SharedArrayBuffersを含む、より高度な手法が利用可能ですが、それらをカバーすることは私の目的ではありません。それらに興味がある場合は、 ここ をお読みください。


スレッド関数

それが標準が私たちにもたらすものです。ただし、ES6は、オンデマンド呼び出し可能Threaded-Functionを実装するのに十分なツールを提供します。

Blobからワーカーを構築でき、関数を(URL.createObjectURLを使用して)に変換できるため)、メッセージを処理し、自然な相互作用を得るために、両方のスレッドに何らかの通信層を実装するだけで済みます。

Promisesはもちろん、すべてが非同期に発生することを考えると、あなたの友達です

この理論を適用すると、説明するシナリオを簡単に実装できます。


私の個人的なアプローチ:ParallelFunction

私は最近、あなたが説明したことを正確に実行する小さなライブラリを実装して公開しました。 2KB未満で(縮小)。

これはParallelFunctionと呼ばれ、 githubnpm 、およびいくつかの-で使用できます。 CDN

ご覧のとおり、これはあなたの要求と完全に一致します。

// 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が返され、解決されます。

ちなみに、他にもたくさんのライブラリがあります。

0
colxi