Kanasan.JS JSDeferred コードリーディング2009年06月30日 23時55分

すっかり記事を書くのが遅れてしまいましたが、Kanasan.JS JSDeferred コードリーディングへ行ってきました (参加者ブログ記事一覧)。JSDeferredcho45 さん作の、非同期処理を簡単に記述するためのライブラリです。簡単なリファレンスもありますが、実際に動かせるサンプルのほうがどんなものか感覚をつかめると思います。

Deferred オブジェクト

JSDeferred では、ひとつの処理をひとつの Deferred オブジェクトとして表現し、処理の流れは Deferred オブジェクトを順につなげた Deferred チェーンで表します。Deferred オブジェクトは三つのプロパティを持ちます。

callback.ok プロパティ
(正常) 処理の本体を表す関数。
callback.ng プロパティ
例外処理の本体を表す関数。
_next プロパティ
Deferred チェーン上の次の処理 (継続) を表す Deferred オブジェクト。

さらに、Deferred オブジェクトの実行状態を意識すると、JSDeferred を使って書かれたコードが読みやすくなるのではないかと思います。

待機 (idle) 状態
処理が実行されない状態。call/fail メソッドが呼び出されると実行中状態へ移る。
実行待ち (waiting) 状態
処理がそのうち実行される状態。cancel メソッドが呼び出されると待機状態へ移る。
実行中 (running) 状態
正常処理または例外処理が実行されている状態。処理本体の実行が終了したら待機状態へ移る。このとき、処理本体の返り値が Deferred オブジェクトなら、その Deferred オブジェクトの継続 (_next プロパティ) を自身の継続と同じにする。そうでなければ、自身の継続たる Deferred オブジェクトを実行する (実行中状態へ移す)。

わかったこと、注意すべきこと

  • 単に new Deferred() または Deferred() とすると、待機状態の Deferred オブジェクトが作られる。
  • 処理本体で Deferred オブジェクトが返されたなら、自身の継続を (直接は) 実行しない。
  • parallel 関数は飛ばして、先に next 関数、wait 関数を読んだほうがよさそう。
  • next 関数 (Deferred.next) と next メソッド (Deferred.prototype.next) は別物。
    • next 関数は、実行待ち状態の Deferred オブジェクトを新規作成する。引数に渡された関数がその Deferred オブジェクトの処理本体となる。
    • next メソッドは、待機状態の Deferred オブジェクトを新規作成し、それを自身の継続とする。引数に渡された関数がその Deferred オブジェクトの処理本体となる。自身の実行状態は変化しない。
  • next 関数については、とりあえず next_default だけを見れば十分。
  • 他の記事では、「JSDeferredがやっとわかった - by edvakf in hatena」が詳細でわかりやすい。

Deferred チェーンの図示

next(function f() {
  ...
  return next(function g() {
    ...
  });
}).
next(function h() {
  ...
});

は、最初に次のような Deferred チェーンを作ります。

[図 1: 上記コードが最初に作る Deferred チェーン]

ここで、Deferred チェーン先頭の Deferred オブジェクトの処理が終了し、f が Deferred オブジェクトを返すと、Deferred チェーンは次のようになります。

[図 2: 上記コードが f 実行後に作る Deferred チェーン]

h (を処理本体とする Deferred オブジェクト) は f (を処理本体とする Deferred オブジェクト) の継続として実行されるわけではありませんが、g (を処理本体とする Deferred オブジェクト) の継続として実行されるので、結局 fgh の順番で処理が進むことになります。

また、f で待機状態の Deferred オブジェクトを返せば、処理の流れをいったん f で止めておき、明示的にその Deferred オブジェクトの call メソッドを呼び出したときに h から処理の流れを再開するといったことができます。