HTML 5新機能のWeb Worker

14896 ワード

1、概要
JavaScript言語は、単一スレッドモデルを採用しています.つまり、すべてのタスクが1つのキューに並んでいて、一度に1つのことしかできません.コンピューターのコンピューティング能力が向上するにつれて、この点は大きな不便をもたらし、JavaScriptの潜在能力を十分に発揮できない.さらに、File APIがJavaScriptにローカルファイルの読み取りを許可していることを考慮すると、なおさらである.
Web Workerの目的は、JavaScriptのマルチスレッド環境を作成し、メインスレッドがサブスレッドにタスクを割り当てることを許可することです.プライマリ・スレッドが実行されると同時に、サブスレッドはバックグラウンドで実行され、両者は互いに干渉しません.サブスレッドが計算タスクを完了するまで待って、結果をメインスレッドに返します.そのため、各サブスレッドは「労働者」(worker)のように、黙って自分の仕事を完成します.
Web Workerには、次のような特徴があります.
  • 同ドメイン制限:サブスレッドにロードされたスクリプトファイルは、メインスレッドのスクリプトファイルと同じドメインにある必要があります.
  • DOM制限:サブスレッドがページを読めないDOMオブジェクト、すなわちdocument、window、parentなどのオブジェクトは、サブスレッドが得られません.(ただし、navigatorオブジェクトとlocationオブジェクトは取得できます.)
  • スクリプト制限:サブスレッドはWebページのグローバル変数と関数を読み取ることができず、alertメソッドとconfirmメソッドを実行することもできませんが、setIntervalとsetTimeoutを実行したり、XMLHttpRequestオブジェクトを使用してAJAXリクエストを発行したりすることができます.
  • ファイル制限:サブスレッドはローカルファイルを読み取ることができません.すなわち、サブスレッドはローカルファイルシステム(file://)を開くことができません.ロードされたスクリプトは、ネットワークから必要です.

  • 使用する前に、ブラウザがこのAPIをサポートしているかどうかを確認します.サポートされているブラウザには、IE 10+、Firefox 3.6+、Safari 4.0+、Chrome、Opera 11が含まれていますが、携帯電話のブラウザではサポートされていません.
    if (window.Worker) {
        //   
    } else {
        //    
    }

    2、サブスレッドの新規作成と起動
    プライマリ・スレッドの内部では、newコマンドを使用してWorkerメソッドを呼び出し、サブスレッドを新規作成できます.
    var worker = new Worker('work.js');

    Workerメソッドのパラメータはスクリプトファイルであり、このファイルはサブスレッドが完了するタスクであり、上のコードではworkである.js.サブスレッドはローカルファイルシステムを読み込めないため、このスクリプトファイルはネットワーク側から読み込まなければなりません.ダウンロードが成功しなかった場合、例えば404エラーが発生すると、このサブスレッドは黙って失敗します.
    サブスレッドが新規作成された後は起動しません.メインスレッドがpostMessageメソッドを呼び出すのを待つ必要があります.つまり、信号が送信されてから起動します.
    worker.postMessage('hello world');

    postMessageメソッドのパラメータは,主スレッドがサブスレッドに伝達する信号である.文字列でもオブジェクトでも構いません.
    worker.postMessage({method: 'each', args: ['work']});

    3、サブスレッドのイベント傍受
    サブスレッド内には、メッセージイベントを傍受するコールバック関数が必要です.
    //File: work.js
    
    self.addEventListener('message', function(e) {
    
        self.postMessage('You said: ' + e.data);
    
    }, false);

    selfはサブスレッド自体を表す、self.addEventListenerは、サブスレッドのmessageイベントにコールバック関数を指定する(onmessage属性の値を直接指定してもよい).コールバック関数のパラメータはイベントオブジェクトであり、dataプロパティにはプライマリスレッドからの信号が含まれます.self.postMessageは,サブスレッドがメインスレッドに信号を送信することを示す.
    サブスレッドは、メインスレッドから送信される信号値に応じて、異なる方法を呼び出すことができる.
    // File: work.js
    
    self.addEventListener('message', function(e) {
        var method = e.data.method;
        var args = e.data.args;
    var reply = doSomething(args); self.postMessage({method: method, reply: reply}); }, false);

    4、メインスレッドのイベント傍受
    プライマリ・スレッドもmessageイベントのコールバック関数を指定し、サブスレッドからの信号をリスニングする必要があります.
    // File: main.js
    worker.addEventListener('message', function(e) {
        console.log(e.data);
    }, false);

    5、エラー処理
    プライマリ・スレッドは、サブスレッドにエラーが発生したかどうかをリスニングできます.エラーが発生すると、プライマリ・スレッドのerrorイベントがトリガーされます.
    worker.onerror(function(e) {
        console.log(e);
    });
    
    // or
    worker.addEventListener('error', function(e) {
        console.log(e);
    }, false);

    6、サブスレッドを閉じる
    使用が完了すると、システムリソースを節約するために、メインスレッドでterminateメソッドを呼び出し、サブスレッドを手動で閉じる必要があります.
    worker.terminate();

    サブスレッドの内部で自身を閉じることもできます.
    self.close();

    7、メインスレッドとサブスレッドのデータ通信
    プライマリ・スレッドとサブスレッドの間の通信内容は、テキストであってもオブジェクトであってもよい.なお、このような通信はコピー関係であり、すなわち、アドレスではなく値を伝達し、サブスレッドによる通信内容の変更は、メインスレッドに影響を及ぼさない.実際、ブラウザ内部の動作メカニズムは、まず通信内容をシリアル化し、シリアル化された文字列をサブスレッドに送り、後者はそれを復元することです.
    プライマリ・スレッドとサブスレッドの間では、File、Blob、ArrayBufferなどのバイナリ・データを交換したり、スレッド間で送信したりすることもできます.
    ただし、コピー方式でバイナリデータを送信すると、パフォーマンスに問題が発生します.たとえば、メインスレッドがサブスレッドに500 MBのファイルを送信すると、デフォルトではブラウザが元のファイルのコピーを生成します.この問題を解決するため、JavaScriptでは、プライマリ・スレッドがバイナリ・データをサブスレッドに直接移行することを許可していますが、いったん移行すると、プライマリ・スレッドはこれらのバイナリ・データを使用できなくなります.これは、複数のスレッドが同時にデータを変更するトラブルを防止するためです.このデータを転送する方法をTransferable Objectsと呼ぶ.
    このメソッドを使用する場合、postMessageメソッドの最後のパラメータは、前に送信した値がサブスレッドに移行できるかどうかを指定する配列でなければなりません.
    worker.postMessage(arrayBuffer, [arrayBuffer]);

    8、同ページのWeb Worker
    通常、サブスレッドは個別のJavaScriptファイルをロードしますが、メインスレッドと同じページのコードをロードすることもできます.Webページのコードを次のように仮定します.
    <!DOCTYPE html>
        <body>
            <script id="worker" type="app/worker">
    
                addEventListener('message', function() {
                    postMessage('Im reading Tech.pro');
                }, false);
            </script>
        </body>
    </html>

    ページのscriptを読み取り、workerで処理できます.
    var blob = new Blob([document.querySelector('#workere').textContent]);

    ここではコードをバイナリデータとして読み込む必要があるので、Blobインタフェースを使用します.次に、このバイナリオブジェクトをURLに変換し、このURLでworkerを作成します.
    var url = window.URL.createObjectURL(blob);
    
    var worker = new Worker(url);

    イベントリスニングコードを配備します.
    worker.addEventListener('message', function(e) {
        console.log(e.data);
    }, false);

    最後にworkerを起動します.
    worker.postMessage('');

    ページ全体のコードは次のとおりです.
    <!DOCTYPE html>
        <body>
            <script id="worker" type="app/worker">
    
                addEventListener('message', function() {
                    postMessage('Work done!');
                }, false);
    
            </script>
    
            <script>
                (function() {
    
                    var blob = new Blob([document.querySelector('#worker').textContent]);
    
                    var url = window.URL.createObjectURL(blob);
    
                    var worker = new Worker(url);
    
                    worker.addEventListener('message', function(e) {
                        console.log(e.data);
                    }, false);
    
                    worker.postMessage('');
                })();
            </script>
        </body>
    </html>

    メインスレッドとサブスレッドのコードは同じページに表示されます.
    上記のWeb Workerはいずれも特定のページに属しており、そのページが閉じるとワークは自動的に終了します.このほか、複数のブラウザウィンドウが同じworkerを共有できる共有Web Workerもあり、すべてのウィンドウが閉じている場合にのみ終了します.このような共有型のWorkerはSharedWorkerオブジェクトで作成されるが,適用される場合が少ないため,ここでは省略する.
    9、参考リンク
    [1] Matt West, Using Web Worker to Speed-Up Your JavaScript Applications
    [2] Eric Bidelman, The Basics of Web Workers
    [3] Eric Bidelman, Transferable Objects: Lightning Fast!
    [4] Jesse Cravens, Web Worker Patterns
    [5] Bipin Joshi, 7 Things You Need To Know About Web Workers
    [6] ruanyf, Web Worker