jQuery.Deferred を使ってみる。
カーテンのない家には住めない事に気がついたminamiです。
jQueryにはjQuery.Deferred というオブジェクトがあります。jQuery 1.5 のころから実装されてだいぶ経っているのですが、今までなんとなくよくわからなくて苦手意識があったので改めて使ってみました。
jQuery.Deferred ってなに?
"deferred" の意味を調べると「延期された」「遅延された」などの意味があります。乱暴にまとめてしまうと、様々な処理が終わった後に呼ばれる(遅延された)処理を監視してくれるオブジェクトと言えます。
何を言っているのかわからん
文章で書くとまどろっこしいのでサンプルを作りました。1~4の処理が全て終わったら完了の表示を出します。
- window.setTimeout() などのタイマー処理
- jQuery.ajax などを使った非同期通信
- jQuery.animate などを使ったアニメーション処理
など、いつ処理が完了するか確実にはわからない処理を、jQuery.Deferred オブジェクトが監視してくれているわけです。jQuery のajax やアニメーションには処理が完了したら行うコールバックの仕組みも用意されていますが、
○○を実行→完了→○○を実行→完了→△△を実行...
のような直列の処理を書こうとするとコールバックの入れ子がどんどん深くなってしまいます。
jQuery.Deferred を使うと、このような直列の処理はもちろん、「任意の処理が全て終わったら○○を実行する」「任意の処理のうちどれかが失敗したら○○を実行する」のような並列の処理もキレイにかけます。
基本的な使い方
まずは処理を監視するjQuery.Deferred オブジェクトのインスタンスを作成します。
var dfd = jQuery.Defered();
jQuery.Deferredオブジェクトには3つの状態があります。基本的な概念としては、「状態を変化させる / 状態に合わせたコールバックを実行する」これだけです。
-
pending
初期状態
-
resolved
処理が終わって完了した状態
- rejected
処理が成功しなかった状態(Ajaxのリクエストが失敗した場合など)
一度pending → resolve / reject に移行するとそこからpending 状態に戻ることはできません。
状態を移行する
deferred.resolve() / deferred.resolveWith()
Deferredオブジェクトをresolvedの状態にします。doneCallbacks のコールバック関数を実行します。
deferred.reject() / deferred.rejectWith()
Deferredオブジェクトをrejectedの状態にします。failCallbacks のコールバック関数を実行します。
deferred.notify() / deferred.notifyWith()
呼び出した際にDeferredオブジェクトがpendingの状態であれば、progressCallbacks のコールバック関数を実行します。
コールバックの登録
deferred.done()
処理が完了した場合(resolved 状態)に実行されるコールバック関数を登録
deferred.fail()
処理が完了しなかった場合(rejected 状態)に実行されるコールバック関数を登録
deferred.progress()
pending 状態の際に呼び出すコールバック関数を登録
deferred.then()
resolved / rejected / pending のコールバック関数をまとめて書けます。
deferred.always()
resolved / rejected いずれかの状態に変化した際のコールバック関数を登録
deferred.promise()
deferred.promise() を使うと、時間のかかる処理の結果を返すことを「約束」できます。ajaxなどの処理で成功(resolved)するか失敗(rejected)するかわからない処理を渡す場合、後から処理の結果を渡すことができます。
jQuery.when()
引数を複数渡して、全ての引数の処理が終わったらコールバックを実行というような並列の処理ができます。Deferredオブジェクトも引数に渡せるため、promise()を返しておくとajaxなどの処理が混ざっていても全て終わったら実行、という処理が書けます!上記サンプルでも使っています。
まだいろいろあるが
基本的にはこのへんを押さえておけば使っていけそうです。
jQuery.Deferredが導入された段階で、jQuery.ajaxやjQuery.animate は、Deferredオブジェクトを返すようになっています。非同期の処理のコールバックがぐっと書きやすくなっていますね。
まとめ
ややこしいイメージでしたが、意外と理解できた気がします!間違っている部分などあればご指摘ください。。
応用すれば画像のプリロード処理なんかもスマートに書けます。もっと使っていきたいです!