« Japanize - ウェブサイトの高速化 | メイン | Japanize が Firefox にフィッシングサイト扱いされている件について »
2007年05月25日
JavaScript/1.7 で協調的マルチスレッド
JavaScript/1.7 の話なので Firefox 限定です。以前、
残念ながら、JavaScript には非同期メッセージを同期化する方法はない!残念!><ほんとに残念!JavaScript 1.7 なら yield でできるんじゃないかと思いました。
Kazuho@Cybozu Labs: JavaScript で非同期処理
と書いたっきり失念していたのですが、社内で話題になったので実装してみました。こんな感じです。
function runnable(f) { var o; o = f(function () { o.next(); }); o.next(); } runnable(function (next) { // 初期化 var answer = Math.floor(Math.random() * 10 + 1); // ボタンが押されるまで yield で待機するよう設定 document.getElementById('guessme_button').onclick = next; // ループ while (1) { yield; var guess = document.getElementById('guessme_value').value; if (guess < answer) { alert('もっと大きいよ'); } else if (guess > answer) { alert('もっと小さいよ'); } else { alert('大正解!'); break; } } // リプレイ document.getElementById('guessme_value').value = ''; runnable(arguments.callee); });
見てのとおり、1-10 の間の数字を当てるゲームです。↓で遊べます。
正直、最初はあまり便利だと思わなかったのですが、ちょっといいかもと思うようになってきました。例えば Firefox の拡張機能で、多数の URL からデータをダウンロードする場合を考えてみます。同期的な処理を行うと、ダウンロードが完了するまで UI をブロックしてしまうため、非同期処理で実装する必要があります。なので、普通はこんな感じで書くことになります。
一般的な書き方function get_all(urls) { if (urls.length != 0) { // build request var xhr = new XMLHttpRequest(); xhr.open("get", urls.shift(), true); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { // handle response if (xhr.status == 200) { ... } // initiate next request get_all(urls); } }; // send request xhr.send(null); } }
慣れればどうってことはないのですが、再帰的な処理を行う必要があります。
それに対し、yield を使うと以下のように書くことができます。
yield を使った書き方function get_all(urls) { runnable(function (next) { for (var i = 0; i < urls.length; i++) { // build request var xhr = new XMLHttpRequest(); xhr.open("get", urls[i], true); xhr.onreadystatechange = function () { if (xhr.readyState == 4) next(); }; xhr.send(null); // wait for response yield; // handle response if (xhr.status == 200) { ... } } }); }
再帰呼び出しの必要がなくなりました。すべてのダウンロードが完了するまでスコープの移動がないので、変数の取り回しが楽になります。それにしても協調型の並列処理なんて、もうとっくに墓場送りになっているものだと思っていましたが、まだまだ使う機会があるんですね (笑)
なお、復帰条件の設定と yield の呼び出しが分かれていて気持ち悪いという向きには、yield の引数として条件を渡すという手もあります。yieldをwait代わりに使っちゃっていいのかなー - outsider reflex は、復帰条件を時間経過に限定するかわりに、そうしているようです。
投稿者 kazuho : 2007年05月25日 13:47
トラックバック
このエントリーのトラックバックURL:
http://labs.cybozu.co.jp/cgi-bin/mt-admin/mt-tbp.cgi/1295
このリストは、次のエントリーを参照しています: JavaScript/1.7 で協調的マルチスレッド:
» [javascript]yieldを使って非同期->同期(続き) from snippets from shinichitomita’s journal
元ネタ: http://labs.cybozu.co.jp/blog/kazuho/archives/2007/05/coopthread.php これ... [続きを読む]
トラックバック時刻: 2007年05月29日 00:27