とある角度から

お腹いっぱいたべられる幸せ

jQuery.Deferredをちゃんと理解する

ちゃんと理解するために、ちゃんと説明してみた。

jQuery.Deferredは、タスクを管理する仕組み

jQuery.Deferredでできることは、以下のような事です。

  1. お仕事の約束をする
  2. お仕事が終わったら結果を受け取る
  3. 受け取った結果を元に次の作業をする

これって、タスク管理ですね。
自分としてはこの書き方が一番理解しやすい気がします。

実は、jQuery.ajax()も内部でDeferredオブジェクトを使っています。
でも、古いjQueryをサポートするために、Deferredオブジェクトを使わない書き方も出来ちゃいます。
Deferredオブジェクトを使わない場合と、使う場合で書いてみます。

Deferredオブジェクトを使わない

従来の、コールバックに次の処理を書いてく感じです。
読み込むURLが増えれば増えるだけ死にたくなります。

Deferredオブジェクトを使う

jQuery.ajax()の戻り値はpromise Object(deferred.promise()の戻り値)になってます。
これを使うと、メソッドチェーンを使ってこんな感じで書けます。
このdone(),fail()関数は、Deferredオブジェクトのメソッドです。
(詳しくはDeferred Object | jQuery API Documentation

  • deferred.done():タスクが成功したときに実行する関数を指定
  • deferred.fail():タスクが失敗したときに実行する関数を指定

実際の使い方

ではここから、実際にDeferredオブジェクトを使って、タスク管理の仕組みを作っていきます。

タスクの処理側を作ってみる

jQuery.ajax()の内部でしている事を理解するため、自分で作った関数に置き換えてみます。

func1は、2秒後に0,1の乱数判定をして、1で成功(resolve)、0で失敗(reject)を返す関数です。
実行すると大体50%の確率で、成功なら「success!」、失敗なら「failed...」と言われますね。

ここで、10-12行を観ると、$.ajax(...)をfunc1()に置き換えた形になってます。
つまり、jQuery.ajax(...)の中でも、func1()と同じようなことをしてます。
これがタスクの処理側の基本的なしくみです。

  • deferred.resolve():タスクが正常に終わったことを通知(引数に指定した内容が、done()に引き渡される)
  • deferred.reject():タスクが異常終了した事を通知(引数に指定した内容が、fail()に引き渡される)
  • deferred.promise():タスク(タスクをこなしますという約束)
Deferredオブジェクトの便利さを実感する

次にこのタスクを3回連続で行ってみます。
コールバックだとネストinネストinネストになるので、ちょっとカオスるやつですね。

タスク終了時に、再度タスクを実行して、そのpromise Objectをreturnしてあげれば、
メソッドチェーンでどんどん次の処理を記述できるので、どんどん下に追記して行けますね。
3つどころか、いくつでも来なさいって感じ。どんどんやっちゃってー

  • then():タスクが終了した時に、結果がresolveなら第1引数の関数、rejectなら第2引数の関数を実行
Deferredオブジェクトの便利さを痛感する

次にこの3つのタスクを並列で行ってみます。
コールバックだと、3つとも終わったタイミングを検知するためにちょっと試行錯誤が必要ですね。

jQuery.when()は、渡されたタスクを並列で実行し、全てが成功したタイミングでdone()、1つでも失敗したタイミングでfail()を実行してくれます。
これを使う為にjQuery.Deferredを作ったんじゃねーかと思ってます。

ちなみにfailに渡される引数は1つで、reject対象の引数のみが渡されます。
以下のソースで確認できます。

渡された変数を全て出力してみると、成功時は3回、失敗時1回だけになりますね。

実用的な何かを作ってみる

Deferredオブジェクトを使って、画像のプリロードをしてみました。

読み込む画像の数が何個増えたって怖いものなしです。
ajax処理と並行する事もできます。Deferred万歳!