RubyのThreadもある意味そうだけど、Delayerなんかもそう。これはJavaScriptでいうところのSetTimeout(callback, 0)みたいなもので、用途もウィジェットの更新とかなので被ってる。
で、JavaScriptではJSDeferredが便利だ
http.get('some_twitter_api').next(function(){ // success }).error(function(){ // fail })
いやーすばらしい。これはパクらない手はない
deferredは、連続した処理をぼちぼち順番にやっていって、エラーの捕捉もしやすい。で、http通信のメソッドとかがDeferredオブジェクトを返せば同じようにつかえて便利だなぁ。
ちょっと実験してみましょう。
現在のmikutterでは
例えばTwitterAPIを叩くのはPost#call_apiというメソッドを使う。こいつはAPI名とブロックを引数に取って、新しくスレッドを作ってリクエストを発行して、そのスレッドを返す。
あとでリクエストが完了したらコールバックを呼ぶ。という、JavaScriptのXMLHttpRequestだっけ、あれみたいな挙動をしている。スレッドを返す理由は、途中でThread#killを使ってキャンセルできるようにという深遠な理由がある。
Deferredを使うとどうなる
まず、今回の実験では、Deferredableをincludeした全てのクラスがdeferredみたいに振舞うことにした。これで、以下のオブジェクトが等しくDeferredのように振る舞える。
- Delayer
- SerialThread
- Thread
ということは、Deferredのブロックの戻り値にThreadとかを設定できてしまう
deferred{ post.call_api(:api) }.next{ |value| // success }.trap{ |exception| // fail }
Proc#call_apiの戻り値はThreadなので、本当は以下のように書ける
post.call_api(:api).next{ |value| // success }.trap{ |exception| // fail }
おなじこと。
こうする利点は、whenとかloopとかがつかえることかな
Deferred.when(deferred_1, deferred_2, ..., deferred_n).next{ // do something }
こういう感じで、複数のDeferredableの終了を待てる。今までだったらDelayerとThread両方が終わったら…なんてコードはよう書かなかったんだけど、Deferredableならできますよどうですか!
Enumerableにeach_deferとか作っておけば、イベントで大量のつぶやきを受け取ったとき、時々休みながら実行ということができる。つまり、ループが長引いたら一旦処理を中断するというあれ。なかなかいいんじゃないかな
とまあ適当なことを書き散らしたけれど、まあ利点は本家とたいして変わらない。DeferredableをincludeすればどんなオブジェクトでもDeferredになれるので、いろんなクラスのエラー処理をとてもスマートに書ける可能性がありますね。
まだこれはコミットせずに様子を見てる。プラグインが書きやすくなるだろうか。