Web Workers用と通常のJavaScript用のコードを共存させる

uupaa2010-06-09



Web Workers の調べ物してました。
Web Workers は、Google Chrome 4+, Firefox3.5+ , Safari4+ で既に利用可能です。

Google Chrome5 では、Web Workers 内で Web Socket を動かすこともできます。

js の基本であり鬼門といえば this オブジェクト

各ポイントで this.toString() や self.toString() を行い、this や self が何を指しているのか調べました。

<script>
var globalScope = this.toString()
</script>
// WebWorkers.js
var workersGlobalScope = this.toString();
var self1 = self.toString();

onmessage = function(event) {
    var workersMethodScope = this.toString();
    var self2 = self.toString();
    postMessage([workersGlobalScope, workersMethodScope, self1, self2].join(","));
};
Google Chrome6 Firefox3.6 Safari5
globalScope [object DOMWindow] [object Window] [object DOMWindow]
workersGlobalScope [object global] [object DedicatedWorkerGlobalScope] [object DedicatedWorkerContext]
workersMethodScope [object DedicatedWorkerContext] [object DedicatedWorkerGlobalScope] [object DedicatedWorkerContext]
self1 [object global] [object DedicatedWorkerGlobalScope] [object DedicatedWorkerContext]
self2 [object global] [object DedicatedWorkerGlobalScope] [object DedicatedWorkerContext]

Web Workers の中で利用可能なオブジェクト

  • WorkerGlobalScope.importScripts() URLを指定するとJavaScriptコードをダウンロードし include します。複数指定可能です
    • 相対パスを解決し、複数のファイルをロードするには、↓のようにします。
var path = location.pathname.split("/"); // location.pathname = "/dir1/dir2/file.ext"
path.shift();                              // 先頭の空要素を捨てる
path.pop();                                // file.ext を捨てる
var dir = path.join("/") + "/";          // dir = "dir1/dir2/"

importScripts(dir + "src.js", dir + "src2.js");
  • WorkerGlobalScope.navigator これは window.navigator 相当のオブジェクトです。ブラウザの切り分けに使います。
    • window.navigator.oreore = 1 としても WorkerGlobalScope.navigator.oreore は undefined です。別物ですから。
  • WorkerGlobalScope.setTimeout, WorkerGlobalScope.setInterval, WorkerGlobalScope.clearTimeout, WorkerGlobalScope.clearInterval タイマーです。
  • WorkerGlobalScope.self これは WorkerGlobalScope の alias です。
  • WorkerGlobalScope.this これは WorkerGlobalScope です。通常の Windows とは別のオブジェクトです。
  • WorkerGlobalScope.onmessage(event) は Worker.postMessage(data) から呼び出されます。
  • WorkerGlobalScope.postMessage(data) は Worker.onmessage(event) をコールバックします。
  • WorkerGlobalScope.location は window.location 相当です。このオブジェクトも window.location とは別物で、毎回新しく作られます。
  • WorkerGlobalScope.close() はスレッドのシャットダウンを開始します。
    • 終了処理中は、WorkerGlobalScope.closing が true になります。
    • 終了すると Worker.onerror(event) をコールバックします。
  • WorkerGlobalScope.closing は終了処理中に true になります。WorkerGlobalScope.closing が実装されていない環境もあるようです。
  • SharedWorkerGlobalScope.name - 省略
  • SharedWorkerGlobalScope.applicationCache - 省略
  • SharedWorkerGlobalScope.onconnect - 省略
  • SharedWorker.port - 省略

Workers の外で利用可能なオブジェクト

  • Worker.postMessage(data) は WorkerGlobalScope.onmessage(event) をコールバックします。
  • Worker.terminate() は ワーカースレッドを終了させます。

通常のコードと Workerスレッド用のコードを共存させる方法

いくつかの点に注意すると、通常の JavaScript コードと、Web Workers から利用するスレッド用のコードを共存させることが可能です。

  • GlobalScope を取得するには window ではなく this を使う。
  • ナビ子記法でラップする。
(function(globalScope) {

})(this);
  • document オブジェクトなどは存在しないため、Web Workers と共存する部分には、document.write などをべた書きしない
  • alert() なども無く、print デバッグは出来ないことに注意。
  • どうしても中身が知りたい場合は、postMessage() で配列を返すようにし、ついでに知りたいオブジェクトも渡せるようにしておく
onmessage = function(event) {
    postMessage([result, "debug text"]);
};

Workerスレッドとして動作しているかを判定する方法

// var isWorker = !!self.postMessage;
var isWorker = !!self.importScripts;

Google Chrome ではデバッグも可能なんだけど、ちょっとした注意も必要

Google Chrome の Developer Tools では Web Workersデバッグも可能です(図を参照)。Workers と書かれた横にあるチェックボックスをチェックすると、iframe 上で擬似的に Workers を動かすモードになります。この状態で動かすと this が window になります。

onmessage = function(event) {
    postMessage(this.toString()); // [object DOMWindow]
};

window オブジェクトに依存したコードを書いていると、チェックを外したとたんに動かなくなりますので、注意が必要です。


SharedWorker の説明は端折りましたが、大体こんな感じです。
間違っている箇所があれば教えてください。

var path = location.pathname.split("/"), // location.pathname = "/dir1/dir2/file.ext"
    dir = path.slice(1, path.length - 1).join("/") + "/"; // dir = "dir1/dir2/"

という手もあったか