« 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:
https://labs.cybozu.co.jp/cgi-bin/mt-admin/mt-tbp.cgi/1295
このリストは、次のエントリーを参照しています: JavaScript/1.7 で協調的マルチスレッド:
» [javascript]yieldを使って非同期->同期(続き) from snippets from shinichitomita’s journal
元ネタ: https://labs.cybozu.co.jp/blog/kazuho/archives/2007/05/coopthread.php これ... [続きを読む]
トラックバック時刻: 2007年05月29日 00:27