Kanasan.JS JSDeferred コードリーディング ― 2009年06月30日 23時55分
すっかり記事を書くのが遅れてしまいましたが、Kanasan.JS JSDeferred コードリーディングへ行ってきました (参加者ブログ記事一覧)。JSDeferred は cho45 さん作の、非同期処理を簡単に記述するためのライブラリです。簡単なリファレンスもありますが、実際に動かせるサンプルのほうがどんなものか感覚をつかめると思います。
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
だけを見れば十分。next_faster_way_readystatechange
、next_faster_way_Image
は、特定の環境用に高速化を施した版。詳細は「JSDeferred を高速化する (試し中) - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech」を参考に。
- 他の記事では、「JSDeferredがやっとわかった - by edvakf in hatena」が詳細でわかりやすい。
Deferred チェーンの図示
next(function f() {
...
return next(function g() {
...
});
}).
next(function h() {
...
});
は、最初に次のような Deferred チェーンを作ります。
ここで、Deferred チェーン先頭の Deferred オブジェクトの処理が終了し、f
が Deferred オブジェクトを返すと、Deferred チェーンは次のようになります。
h
(を処理本体とする Deferred オブジェクト) は f
(を処理本体とする Deferred オブジェクト) の継続として実行されるわけではありませんが、g
(を処理本体とする Deferred オブジェクト) の継続として実行されるので、結局 f
、g
、h
の順番で処理が進むことになります。
また、f
で待機状態の Deferred オブジェクトを返せば、処理の流れをいったん f
で止めておき、明示的にその Deferred オブジェクトの call
メソッドを呼び出したときに h
から処理の流れを再開するといったことができます。
最近のコメント