Web Workers用と通常のJavaScript用のコードを共存させる
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 とは別物で、毎回新しく作られます。
- { protocol: "http:", href: "http://localhost/msgpack.worker.js", host: "localhost", pathname: "/msgpack.worker.js", hostname: "localhost", port: "", search:"", hash:"" } といった情報を保持しています。
- 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/"
という手もあったか