そんなJavaScriptですが、コーディングしてChromeやSafariなどでWebInspectorを開いてデバッグをしているとグローバルスコープに気になるオブジェクトを見かけることがあります。
Workerとあります。みなさんもC++などでマルチスレッドなプログラムを組んだり、GUIつきのプログラムを書いているときに「ワーカースレッド」という言葉を耳にしたことはないでしょうか。このWorkerがその「ワーカー」と同じなら、何やらマルチスレッドできそうな「匂い」を感じませんか?感じますよね?
そう、このWorkerコンストラクタこそ、我らがJavaScripterの救世主(になる予定)のマルチスレッド化の秘密兵器なのです。
さて、そんなWorkerオブジェクトですが、一体どうやって使うんでしょうか。結論から言ってしまうと次のように書けば使えます。
//main.js var worker = new Worker("calc.js"); //Workerの処理を記述したファイルのパスを引数にコンストラクタを呼び出す worker.onmessage = function(event){ //Workerからメッセージが返ってきた時の処理を設定する var data = event.data; data.forEach(function(num){ write(num + ", "); }); }; var defs = {a_n : [0, 1], to : 20}; worker.postMessage(defs); //Workerにデータを渡して処理を開始させる
//calc.js onmessage = function(event){ //メインスレッドからデータを受け取って処理する var data = event.data, results = [data.a_n[0], data.a_n[1]]; for(var i = 0; i < data.to; ++i){ //フィボナッチ数列を計算して配列に収める results.push(results[i] + results[i + 1]); } postMessage(results); //処理した結果をメインスレッドに返す };
WebWorkerを使うには最低限.jsファイルが2つ必要です。一つは、Workerを呼び出すメインのファイル(上記の例のmain.jsに当たります)、もうひとつはWorkerがすべき処理を記述したファイル(上記の例でのcalc.js)です。大まかな流れはコメントに書いてあるとおりなので、詳しい解説は不要でしょう。
しかし、その中でも注意すべき点が2つあります。まず、calc.js内のonmessage関数の定義の部分に注目してください。まるでグローバル変数のような定義をしていますが、これはWorkerのスコープとメインスレッドのスコープが分離しているためです。つまり、このonmessage変数はメインスレッドからは参照不可能なわけです。
またこれに伴って通常、ユーザーエージェントでJavaScriptを実行するとグローバルスコープにwindowやDOMなどのオブジェクトが定義されているものですが、Workerが動作するスコープにはこれらのオブジェクトは一切定義されていません。そのため、WorkerからDOM操作などはできません。これは、自らGUIつきのプログラムを書いたことがある人はよく解ると思いますが、好き勝手にGUIをいじるコードを書いていると複数のスレッドが同時にGUIをいじってしまって整合性に破綻をきたしてしまう場合があるので、メインスレッド以外からは一切GUIをいじれないようにするための措置です。
2つ目は、メインスレッドとWorkerでやり取りされるデータの形式です。この二者間のデータのやり取りは、JSON形式でやり取りされているようなので関数を含むオブジェクトをpostMessageに渡すと関数が失われてしまいます。このため、メソッドによる振る舞いの定義が必要なオブジェクトを扱う処理をWorkerでこなすには少し手を加える必要があります。
以上、自サイトで公開しているプログラム内でWebWorkerを使う機会があったので、備忘録も兼ねて記事にしてみました。しかし、その中でも注意すべき点が2つあります。まず、calc.js内のonmessage関数の定義の部分に注目してください。まるでグローバル変数のような定義をしていますが、これはWorkerのスコープとメインスレッドのスコープが分離しているためです。つまり、このonmessage変数はメインスレッドからは参照不可能なわけです。
またこれに伴って通常、ユーザーエージェントでJavaScriptを実行するとグローバルスコープにwindowやDOMなどのオブジェクトが定義されているものですが、Workerが動作するスコープにはこれらのオブジェクトは一切定義されていません。そのため、WorkerからDOM操作などはできません。これは、自らGUIつきのプログラムを書いたことがある人はよく解ると思いますが、好き勝手にGUIをいじるコードを書いていると複数のスレッドが同時にGUIをいじってしまって整合性に破綻をきたしてしまう場合があるので、メインスレッド以外からは一切GUIをいじれないようにするための措置です。
2つ目は、メインスレッドとWorkerでやり取りされるデータの形式です。この二者間のデータのやり取りは、JSON形式でやり取りされているようなので関数を含むオブジェクトをpostMessageに渡すと関数が失われてしまいます。このため、メソッドによる振る舞いの定義が必要なオブジェクトを扱う処理をWorkerでこなすには少し手を加える必要があります。
後日追記:もうちょっと実用的なサンプルをjsdo.itに公開しました。