deferred.el のできるまで:調査、設計と実装
deferred.elを作る上での参考にした情報や、設計・実装のメモです。
一般的なDeferredの理解や、Deferredの情報まとめとしても使えるかもしれません。
Deferredについて
- コールバックをうまく書くイディオム
- 非同期処理の抽象化の道具
- 汎用性が高く、使い始めると無いと困るレベル
後述のbrazilさんの記事が大変オススメです。
参考にした情報など
基本的に古い記事(2年以上前)が多く、JavaScript界の中ではすでにブームは過ぎ去って、常識の域になっているようです。そうは言っても、deferred.elを実装するために過去のブックマークなどを掘り起こして読み直してみたのですが、自分の中では再発見することも多かったです。
JSDeferred
deferred.elで一番参考にした実装です。実装がシンプルです。
後述のMochikit.AsyncのDeferredに触発されてできたようです。
- JSDeferred (GitHub)
- JSDeferredの開発元, cho45さん
- Codereposから移動したみたい
- 最近はNode.js周りの開発が活発
- JSDeferredの開発元, cho45さん
- JSDeferred Sample (cho45さん)
- 書き方サンプル。インタプリタやcallccなど、興味深い例がいっぱいある。
- 特集:JSDeferredで,面倒な非同期処理とサヨナラ|gihyo.jp … 技術評論社
- 本人によるJSDeferredの使い方解説
- JSDeferredがやっとわかった (edvakfさん)
- JSDeferredソース解説など
- その他、各地でJSDeferredのコードリーディングや勉強会が行われています
Mochikit.Async / Twisted
JSDeferredの次に参考にした実装です。機能が豊富です。
Pythonの非同期フレームワークのTwistedからの移植のようです。
- brazilさんシリーズ (Deferredのエントリーの目次)
- 最初のDeferred祭りは2005〜2006頃
- Deferredチェーン、非同期処理の逐次実行
- まず、Deferredの動きの話。絵が非常に分かりやすいです。
- Deferred、遅延リソースのインターフェース、パターン
- 「準備に時間のかかるリソース」のインタフェースの話。
- 「使い出すと、至る所に、それが存在することに気が付くようになりました。」
- Deferred、結果値の保存、非同期にインターフェースを合わせる
- Deferredの本質。コールバック関数を組み合わせる柔軟性について。
- MochiKit.Async.Deferredで非同期処理の同期処理を直感的に書く (kuさん)
- brazilさんのTomblooでの適用例を解説。parallelの待ち合わせなど。
- 産業技術大学院大学でのInfoTalk / 大谷さん@ありえるの資料(PDF)
- Python Twistedですが、「なぜ非同期プログラミングか」についての分かりやすい説明です。
- Mochikit.Async (Mochikit)
- 開発元ドキュメント。Mochikit.Asyncのインタフェースの一覧。
- JSDeferredとかなりインタフェースが異なる。実装もかなり違う。
- MochiKit Deferred と jQuery Deferred の違い (cho45さん)
- 実装の違いについての解説。実装の参考になりました。
その他
みんな非同期や遅延リソースの取り扱いに困っているようで、Deferredにはいろいろな実装があります。
- AS3で非同期処理のオープンソースのライブラリ一覧
- 非同期ライブラリ7個の実装の違い、評価ポイント
- AS3用ですが、どんな機能が必要かなど、API設計・実装の参考になりました。
- RPC サーバの遅延リターン
- ポーリング、コールバックの違い。サーバー側RPCのまとめ。
- deferred.elでは使ってませんが、環境を超えた遅延のインタフェースをどうするかという話が興味深いです。
- Node.js 用の非同期処理を簡単にしてくれるライブラリ async.js
- Node.js時代の非同期ライブラリ3個について調査。この辺は、まだまだ発展途上のように見えます。
設計
いろいろ見た結果、JSDeferredがコード量が少ないながら、十分なインタフェースを備えているようでしたので、JSDeferredをインタフェースと実装のお手本にすることにしました。また、結果の待ち合わせについてはMochikit.Asyncから雰囲気で持ってきました。
すこし残念なのは、Emacs Lisp では JSDeferred にあったようなメソッドチェーンが出来ないことなのですが、ある程度コードの中で目立って、書きやすそうなマクロ(deferred:$)を用意することでカバーすることにしました。
実装
何度かJSDeferredのコードを読んだことがあったので、先にテストを書いてみて、うろ覚えで実装してみました。そうすると、当然ながら仕様の勘違いがいくつかあったのですが、それが元でさらにDeferredを深く理解するきっかけになりました。
テストについては、るびきちさんの el-expectations.el を使いました。こういうライブラリ的なものについてはかなり役立ちます。非同期のテスト対象にはそのまま使えなかったので、マクロでなんとかしました。そのためテスト自体は厳密ではないのですが、それでもかなりのバグを抑えることが出来たと思います。
el-expectaions.el については、どこでエラーが失敗したのかをexpectに説明を書くなどして分かりやすくしたいとか、expectのコードの途中でチェックするためにassertも書けるとテストコードが減ってうれしいなと思いました。
deferred.elはそれほど長くなくて、しかもコアの部分はdeferredオブジェクトを回しているところだけなので、読める人はおもしろいのではないかなと思います(実際に作っていておもしろかったので)。
これで、deferred.elが出来るまでシリーズは終わりです。
次回は、Deferredと一緒に出てくる継続とか遅延実行などについてのもやもやについて、考えてみたことを書いてみます。