WebWorkerを使ってみる。
最近のJavascript関係ではWebSocketとWebWorkerが気になってたんだけど、
WebSocketの方はnode.jsとかJettyとか?サーバ側にも仕掛けが必要なので、
手っ取り早くできる方ってことで、今更ながらWebWorkerを試してみた。
試してみたかったことは次の点。
- 基本的な使い方
- jQueryとかのライブラリが使えるか
- オレオレクラスが使えるか
とりあえずこれらに絞ってお試し。
基本的な使い方
インスタンスを生成して、メッセージでやりとりする。
まず、こんな感じのHTMLを用意して。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Web Worker Test</title> </head> <body> <h1>Web Worker Test</h1> <script type="text/javascript"> // Workerのインスタンスを生成する var w = new Worker('test.js'); // messageイベントのリスナーを追加 w.addEventListener('message', onMessage, false); // イベントリスナー function onMessage(e) { // イベントデータのログ書きだし console.log(e.data); } // 文字列を送信 w.postMessage('Test'); // 数値を送信 w.postMessage(12345); // 真偽値を送信 w.postMessage(false); // 配列を送信 w.postMessage([1, 2, '3']); // オブジェクトを送信 w.postMessage({ x : 100, y : 200 }); </script> </body> </html>
Workerで使うJavascriptファイルを用意する。
// messageイベントのリスナー追加 addEventListener('message', onMessage, false); // イベントリスナー function onMessage(e) { // データ取りだし var msg = e.data; // 受けとったデータをメッセージ送信 postMessage('recive : [' + JSON.stringify(msg) + ':' + typeof(msg) + ']'); }
で、HTMLをブラウザで開くと、得られる結果はこんな感じ。
recive : ["Test":string] recive : [12345:number] recive : [false:boolean] recive : [[1,2,"3"]:object] recive : [{"x":100,"y":200}:object]
Workerのインスタンス生成側とWorker内でメッセージがやりとりされてる。
スコープが異なるからリスナーのonMessage関数は名前だぶってても上書きされない。
Worker内ではconsole.log使えなかった。
jQueryとかのライブラリが使えるか
Workerのスコープ内ではDOMに手出しができないので、
セレクタとかは使えないけど、$.ajaxくらいは使えるかテスト。
Workerを使うHTMLを新たに用意。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Web Worker Test</title> </head> <body> <h1>Web Worker Test</h1> <script type="text/javascript"> // Workerのインスタンスを生成する var w = new Worker('test.js'); // messageイベントのリスナーを追加 w.addEventListener('message', onMessage, false); // イベントリスナー function onMessage(e) { // イベントデータのログ書きだし console.log(e.data); } // メッセージを送信 w.postMessage('get data'); </script> </body> </html>
Worker用のJavascriptファイルを用意。
// 外部ファイル読み込み importScripts('jquery-1.4.3.min.js'); // messageイベントのリスナー追加 addEventListener('message', onMessage, false); // イベントリスナー function onMessage(e) { $.ajax({ url : './test.txt', // 中身はHelloだけのテキスト success : function(data) { postMessage(data); } }); }
で、実行してみると、Firebugのエラーメッセージ。
window is not defined http://localhost/exam/worker/jquery-1.4.3.min.js Line 166
ChromiumのDevelopツールのエラーメッセージ。
test.js:1Uncaught ReferenceError: window is not defined
windowなんてないよ!って怒られる。windowにもdocumentにも手をだせないから、
ライブラリ内でふれちゃってるところがあると、やっぱりだめ。
// messageイベントのリスナー追加 addEventListener('message', onMessage, false); // イベントリスナー function onMessage(e) { var xhr = new XMLHttpRequest(); // 同期モードで送信 xhr.open('GET', './test.txt', false); xhr.onreadystatechange = function() { if(xhr.readyState === 4) { // 受信できたらメッセージを送信 var res = xhr.responseText; postMessage(res); } }; xhr.send(null); }
Worker内なので、同期モードでリクエスト投げてもWorkerの外は影響受けない。
オレオレクラスが使えるか
最後、自作クラスが使えるか確認。
まずはWorkerのインスタンス生成側。
window.onload = function() { // Workerのインスタンス生成(キャッシュきかないようにちょっと対策) var w = new Worker('worker.js?' + (new Date()).getTime()); // メッセージイベントのリスナー追加 w.addEventListener('message', onMessage, false); for(var i = 0; i < 10; i++) { // データを10回通知 w.postMessage({ cmd : 'set', data : 'value' + (i + 1) }); } // 送ったデータで作った文字列を返すように通知 w.postMessage({ cmd : 'get' }); }; // メッセージイベントリスナー function onMessage(e) { // コマンド var cmd = e.data.cmd; if(cmd === 'get') { // ログにデータ書きだし console.log(e.data.data); } }
Worker側のスクリプト
// 外部スクリプト読み込み importScripts('StringBuilder.js'); // メッセージイベントのリスナー追加 addEventListener('message', onMessage, false); // インスタンス生成 var sb = new StringBuilder(); // メッセージイベントリスナー function onMessage(e) { // コマンド var cmd = e.data.cmd; // データ var data = e.data.data || null; switch(cmd) { case 'set' : // 追加 sb.add(data); break; case 'get' : // 文字列にして返す postMessage({ cmd : 'get', data : sb.toString() }); break; default : // コマンドなかったらエラー throw new Error('Operation Not Found. [' + cmd +']'); break; } }
外部読み込みするクラスファイル
/** * 文字列連結クラス */ function StringBuilder() { this.init.apply(this, arguments); } StringBuilder.prototype = { /** * コンストラクタ */ init : function() { // 初期化 this._seq = []; }, /** * 値を追加します * * @param {String|Number|Boolean} s 追加値 */ add : function(s) { // 追加 this._seq[this._seq.length] = s; }, /** * 文字列として取得します * * @return {String} 文字列 */ toString : function() { // 連結 return this._seq.join(''); } };
実行結果は「value1value2value3value4value5value6value7value8value9value10」が問題なく得られる。
今回のはクラスにするまでもない&Worker使うまでもない処理だけど、
windowとかDOMとかに手をつけてなければ、過去の資産も流用できるっぽい。
やってたらまた調べたいこと増えたので、もうちょっと遊んでみるつもり。