jQuery.Defferredとは、非同期プログラミングのJavaScript の制御を、簡単に同期制御したいために存在する。
一言で、jQuery.Defferred
コールバックのネスト地獄からの解放
Before 導入前
calleback hell からの解放というメリットを以下のサンプルで理解すると速い
$.get( "/first.php", function( ) { $.get( "/second.php", function( ) { $.get( "/third.php", function( ) { }) }) })
After 導入後
$.get( "/first.php") .then( function ( ) { return $.get( "/second.php") } ) .then( function ( ) { return $.get( "/third.php") } )
ネストがスッキリ読みやすい。
ネストがスッキリして読みやすくなり、コピペもやりやすくなる。
そしてなにより、Defferredで重要なのが
実行順の保証
実行順が明確になります。処理が終わってから、次の処理呼び出す。この流れを確実に呼び出すことが出来ます。
JavaScriptは非同期実行
JavaScriptは非同期実行される。イベントハンドラは非同期に呼び出される。
つまり、実行順序が分からりづらい。
イベントハンドラの管理は複数人になると大変だ。
実行順は保証されない
次のような、引数秒だけ遅延するPHPを作るのがわかりやすいと思う。
<?php $delay = !empty($_GET["t"]) ? $_GET["t"] : 0; sleep($delay); echo "hello from php with delay $delay";
コレを呼び出す、Ajax処理を書く。
$.get("./delay.php?t=1" , function( data ){ console.log(data)}) $.get("./delay.php?t=3" , function( data ){ console.log(data)}) $.get("./delay.php?t=2" , function( data ){ console.log(data)})
出力結果は
呼び出し順ではなく、イベント順です。
> 1 > 2 > 3
値が帰ってきたものから順にCallbackが呼び出されます。
実行順を保証したい。
ソースコードに書いた順で実行されたい
$.get("./delay.php?t=1" ).done(function(data){ console.log(data) $.get("./delay.php?t=3" ).done(function(data){ console.log(data) $.get("./delay.php?t=2" ).done(function(data){console.log(data)}) }) })
このようにコールバックをネストすれば出来る。
ネストが深くなるの嫌だ #=> then を使う。
$.get("./delay.php?t=1" ).then(function(data){ console.log(data) return $.get("./delay.php?t=3" ) }).then(function(data){ console.log(data) return $.get("./delay.php?t=2" ) }) .done(function(data){ console.log(data) })
>1 >3 >2
意図通りの実行結果が得られた。
このとき、done().done() の代わりに then( return obj ).then(return obj )している。
全部並列にする。
全部の呼び出しが、並列であることを明示して、全部の処理が終わってから次の処理を行う。
$.when( $.get("./delay.php?t=1") ), $.get("./delay.php?t=3" ) ), $.get("./delay.php?t=2" ) ) ).done(function(){ console.log("all done") })
並列にすると例外処理が嬉しい
実行スレッドが一つしか無いが、マルチスレッド・プログラミングが必要
イベントハンドラのデザインパターンはワーカースレッドと、イベントキューになる。
利用シーンには以下の様なものが考えられる。
$.ajaxの結果で切り分けたい時
- 順番に送信したい。
- 送信結果がエラーの時は最初からやり直したい。
- 送信結果がエラーならlocalStorageに保存したい。
クリック系のテストを実行する時
- ボタン1をクリックして
- ボタン2をクリックして
- ボタン3をクリックしたい
アニメーションの実行順をきっちりやりたいとき
- slideyupしてから
- 0.2秒後に
- slidedown する
など、実行順の処理を明確に書くのがとても楽になります。
このような処理を、jQuery本体でDefferredで書けるようになっていて楽にかけます。
resolve() やpromise() / reject() はそのために存在する。自分でresolve/reject 書くと世界が広がる。
まとめ
DefferredはjQueryの内部に当たり前に存在している。
$.ajaxの変化が一番利しやすい。
$.get("/", function(){})
$.ajaxが書かれた上のようなコードは、Defferred導入後に、以下になった。
$.get("/").done( function(){ } ).fail( function(){} )
このとき、Deferred*1が使われている。
Deferredを使うと、ネストが楽に書ける。
さらに、エラー時に確実に fail が実行される。
さらに、全部の処理が終わってから次の処理開始。
を明確に書くのがとても楽。
最後に
メソッド・チェーンで続けるときは return promise()
*1:promise