それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

jQuery.Defferred を簡単なサンプルで覚える。

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の結果で切り分けたい時

  1. 順番に送信したい。
  2. 送信結果がエラーの時は最初からやり直したい。
  3. 送信結果がエラーならlocalStorageに保存したい。

クリック系のテストを実行する時

  1. ボタン1をクリックして
  2. ボタン2をクリックして
  3. ボタン3をクリックしたい

アニメーションの実行順をきっちりやりたいとき

  1. slideyupしてから
  2. 0.2秒後に
  3. 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