yieldでwin32のwaitForSingleObjectみたいなことがしたい(けどできないみたい)
すいませんすごい勘違いしてました。yield+非同期通信自体はできます。
console.log("hello"); var it = function () { var req = (new XMLHttpRequest()); req.onreadystatechange = function () { if (req.readyState == 4) { console.log(", "); it.next(); } } req.open("GET", document.location.pathname, true) req.send(null); yield; console.log("world"); }.call(this); it.next();
でも目的のwaitFor*Objectみたいなふつうには書けなくて、けっきょくまたyieldの入ってる関数の中に後続の処理を書かないといけなくて("world"部分を関数の外には出せない)、それだとdeferredとかわんないじゃん!!というオチ。アホだ。
なんとなくわかった。jsは協調型マルチスレッド的シングルスレッドで動くモデルなんだから、ひとつひとつのイベントはアトミックに実行される命令みたいなもんで、その中にwaitみたいなのを入れるのはモデルとしてそもそも無理なんだと思う。そこでスリープするには明示的なスレッド切り替え許可が必要になるはず。その許可がイベントハンドラからのreturnでしか示せないんだから、任意の地点でwaitするとか不可能。deferredみたいにコーディングに制約をつければ可能。
var it; function xhr() { console.log("hello"); with(new XMLHttpRequest()) { open("GET", "/", true) onreadystatechange = function () { if (readyState == 4) { console.log(", "); it.next() } }; send(null); yield; } console.log("world"); } it = xhr()
って書いたら"hello"が表示されて、xhr()の中のyieldでxhr()から抜けて、しかるべき後 onreadystatechangeでreadyStateが4になって it.next() が呼ばれてyieldのところに戻ってきて "world"が表示されるという、yieldでブロックしないイベント待ちをして、イベント発生後にその後を実行する、みたいなことができるのを期待したけどできませんでした。なんかyieldが入ってると、その関数の中(関数の持ってるクロージャを共有している関数)には、その関数と関連付けられたジェネレータのnext()でブロックされている限り実行されないみたい。非同期イベントから呼ばれたときは、スレッドのイベントキューの中でペンディングされるっぽい(後でfirebug consoleから外側から手動でit.next()を呼んであげたら、onreadystatechangeが何度も呼ばれたので、XHR自体は送信されてるし、レスポンスも帰ってきてる)。
waitFor*Object*1みたいなのあったらDeferredなしできれいに書けるのになー
deferredにすると
with(D()) { console.log("Hello") xhttp.get("/").next( function () { console.log(", ") } ).next( function () { console.log("World") } ); }
こうなって、"world"の部分もdeferredに入れてあげないといけなくなる。既存のライブラリの一部だけをブロッキングじゃないのに置き換えたい、とかいうときはどうしたらいいんだ。
たとえば、ライブラリのコードの中でファイルを読み込む部分があって、そこが4Gのファイルを読むと30秒かかるから非同期にしたいけど、ファイルを読む部分は関数呼び出しネストの奥深くにあるので、ファイルを読んだ後の処理をdeferredで囲むのは無理(なぜならdeferred.nextに渡すべき後処理部分が、ネストレベルの違う複数の関数に処理がまたがっているから)、みたいな時。
WaitFor*Objectがあると一発で解決なんだけど....
pthread_cond_waitとおんなじようなもん。
*1:Win32APIをご存じない皆様のために補足しておくとWaitFor*Objectは特定のイベントオブジェクトがfireするまでsleepして、fireしたときにawakeしてその後の処理が実行されていくものです。たしかWaitForSingleObjectとWaitForMultipleObjectsっていうのがあった