deferred.el の出来るまで:Emacsでの非同期処理
Emacsでの非同期処理は大変
これまでEmacsのGUIアプリをいくつか書いてみて、非同期の処理を何とかしたいと思ったことが deferred.el を書こうと思った動機です。
Emacs Lisp上でコマンドを非同期で処理しようと思うと、コールバックをつなげたり、正しくエラー処理を行うために、かなり長いコードを書く必要があります。また、最近はWebにアクセスする機会も多いのですが、これも非同期で処理するとなるとまた面倒です。非同期をやめて、ブロックする(Emacs全体が固まる)コードで書けば楽なのですが、それではユーザー体験としてマイナスになってしまいます。
特に大変だったのが cacoo.el を作っているときでした。 cacoo.el では、以下のような流れで画像を表示します。
また、オフラインでの動作も考えて、ローカルにキャッシュも持っています。個人的に大量の画像を処理することが多いので、非同期で処理することは必須でした。
これを普通に実装するのは大変なので、cacoo.elを最初に作った時は、簡単な非同期の仕組みを作って実装していました。アドホックでかなり簡単な仕組みだったにもかかわらず、全体のコードのうち相当な行数を占めてしまいました。こういうよくある処理のために毎回非同期の仕組みを準備するのは大変です。
同じシングルスレッドのJavaScriptでdeferredの仕組みが大成功していましたので、同じような仕組みがEmacsにもあるべきだと考えました。
Emacsの非同期処理について調べてみた
いきなり作る前に、まずは調査してみることにしました。その道30年のエディタです。もしかして、自分がEmacsでのうまい非同期の処理方法を知らないだけかもしれないと思い、ローカルのファイルやネット上のリソースを調べてみました。
普通の非同期・同期のまとめ
まず、Emacs上での標準的な方法についてまとめます。
- 外部コマンド起動には同期(call-process)と非同期(start-process)がある
- 同期はブロックする
- 非同期はブロックせずに結果をフィルター関数(コールバックみたいなもの)やバッファで受け取る
- TCPソケットも非同期プロセスと同様に扱う
- 非同期処理が連続したり、多数の非同期プロセスの制御が難しい
- コールバックの連続、待ち合わせ、起動プロセス数の制御など
以下の調査の目的は、この標準の方法以外の、うまい非同期の扱い方をしているものを見つけることです。
java の concurrent パッケージとか、 Mochikit.Async(JSDeferred) のようなものを見つけられればベストです。
ローカルのソースを探してみた
- infoをasyncで検索
- Asynchronous Processes の章
- →普通のコマンド起動の話
ネットで検索してみた
教科書どおりに「Emacsと非同期」「Emacs async」で検索して、出てきた結果を調べてみました。
- 非同期プロセスをEmacsで扱う
- elispでの非同期コマンド起動の解説
- お世話になりました!
- elisp でのネットワークプログラム
- Emacs Lispのダメなところ(あどけない話)
- Emacsはシングルスレッド。非同期の実現は限定的。
- EmacsとAjax(あどけない話)
- AjaxとXHRの比較。細かい制御の出来るfilterは便利。
- Emacs Lispでスクリプト処理 ありえるえりあ
- elispでWebサーバー実装など
- elisp で Google Calendar から祝日を取ってきてみた - 適当めも
- url-retrieveを使って認証付きアクセス
なかなか、基本以外のうまいやり方が出てきません。
今度は非同期を扱ってそうなアプリを調査してみました。
- auto-install.el
- url-retrieveで非同期にバッファで取ってくる
- twittering-mode
- navi2ch
- async-eval.el
- comint.el
- 標準添付、多数のシェル的アプリが利用
- 普通にfilterで受け取っている
設計・実装へ
ということで、作るしかないということが分かりました。
作ると決まれば、どんな物を作るかについて考えるわけですが、どんなインタフェースが良いか、どんな実装にするべきかなど、いろいろ気になることが出てきます。JSDeferredがコードも短くてお手本にするには丁度いいと思っているのですが、もっと他に見ておくべき実装はないかどうか調べてみました。
次回、deferred.el の出来るまで:調査、設計・実装に続きます。