あどけない話

Internet technologies

Emacs と Ajax

巷では、やれ Ajax だとか、RIAだとか騒がれていますが、Ajax の非同期性は基礎がなってない、というお話です。

Emacs

Emacs では、コマンドを start-process で起動したり、TCP コネクションを open-network-stream で開くと、それは非同期プロセスに見えます。こんな感じでコードを書きます。

(defun my-filter (process string)
  ;; string を適当に処理
  )
(defun my-sentinel (process event)
  ;; 終了処理 (異常終了の処理とか)
  )
(defun my-connection ()
  (let ((process (open-network-stream プロセス名 バッファ名 ホスト名 ポート)))
    (set-process-filter process 'my-filter)
    (set-process-sentinel process 'my-sentinel)
    (process-send-string process 命令)))

非同期プロセスから入力があると、その都度フィルタが呼ばれます。このように、通信の途中でフィルタが呼ばれるので、通信の終了を待たずに入力を処理できます。

実装としては、古典的な select() で実現されています。Emacs では、決してリッチな体験はできませんが、基本はとってもしっかりしています。

Ajax

Ajax の典型的なコードは、以下のようになります。

var request = new XMLHttpRequest();
request.onreadystatechange = function() {
    if (request.readyState == 3) {
        // ブラウザによって挙動が違う
    } else (request.readyState == 4) {
	// request.responseText を処理
    }
}
request.open('GET', 'foo.txt', true);
request.send(null);

readyState は、3 が「データの取得中」、4が「データの取得完了」です。Emacs に例えるなら、前者がフィルタ(filter)、後者が番兵(sentinel)となります。

readyState 3 の挙動はブラウザによってマチマチです。Firefox なら、ちゃんと適当なタイミングで指定した関数を読んでくれますが、IE では状態が変わった際に一回だけ関数を呼び、しかも responseText を参照するとエラーになります。

つまり、readyState 3 は事実上使えないのです。

比較してみる

複数のデータを取得したいとしましょう。

クライアントが データのID を指定すると、サーバはデータを返すとします。複数の ID を列挙しておけば、複数のデータが順に返って来ます。

Emacs なら、複数の ID を指定し、フィルタでデータを順に処理して行けます。データの順番は守られます。

Ajax だと、readyState 4 を使うしかないので、複数個の Ajax オブジェクトを生成し、それぞれが ID を 1 つ指定して、戻ってきたデータをそれぞれ処理することになります。データの順番は守られないので、別途データを並べ直す仕組みを考える必要があります。

これでうまくいくアプリケーションもあるでしょう。しかし、順番が守られていないとうまくいかないアプリケーションもあります。

その場合は結局、1 つの Ajax オブジェクトから、複数の ID をサーバへ指定し、すべてのデータを取得した後で、処理することになります。これは、「ユーザの操作」と「通信」は非同期になっていますが、「通信」と「描画」は非同期にならないことを意味しています。

そう考えると、Ajax を標榜しながら、そんな制約のある Web アプリケーションって多いと思いませんか?