undefined

bokuweb.me

【駆逐してやる】jQuery.Defferdについて自分なりに咀嚼してみた【コールバック地獄を】

javascriptってコールバック地獄は避けられないの?という話があったので名前だけ知っていたjQuery.Defferdについて調べてみました。

コールバック地獄?

1秒後に"hoge"その更に2秒後に"fuga"を出力する場合CoffeeScriptではこんな風になったりします。

setTimeout ()->
  console.log "hoge"
  setTimeout ()->
    console.log "fuga"
  , 2000
, 1000

上のコードは簡単な例なのでいいですが、さらに非同期にデータを通信したり、何秒後にこれして、その数秒後にあれしてってなるとどんどん複雑になり、「コールバック地獄」におちいります。 その解決の1つjQuery.Defferdについて調べてみたので記載しておきます。

jQuery.Defferd?

jQuery1.5以降にに備わっている機能のひとつで、プラグインを追加で読み込まなくても使うことができる。 この機能を使うことで「コールバック地獄」から解放することができるようです。

言葉ではなかなかいい表しにくいので上のコードがjQuery.Defferdを使用することでどのように書けるか試してみます。

deferred.done()

func1 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hoge")
    d.resolve() #resolve
  ,1000
  return d.promise()

func2 = ()->
  setTimeout ()->
    console.log("fuga")
  ,2000

promise = func1()
promise.done ()->
  func2() # func1の非同期処理が完了した後、func2が実行される

func1ないのsetTimeoutのコールバック関数内でd.resolve()を呼ぶことで、非同期処理が正常終了とみなしpromise.done()が実行されます。

deferred.fail()

非同期処理に失敗した場合は以下のようにd.reject()を呼ぶことで、非同期処理に失敗したとみなしpromise.fail()が実行されます。

func1 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hoge")
    d.reject()  #reject
  ,1000
  return d.promise()

func2 = ()->
  setTimeout ()->
    console.log("success")
  ,2000

func3 = ()->
  setTimeout ()->
    console.log("failed")
  ,2000

promise = func1()
promise.done ()-> # 非同期処理内でrejecteされた場合doneは実行されない
  func2()
promise.fail ()-> # fail()が実行される
  func3()

deferred.then()

then()を使うことでdone()とfail()をひとまとめにできます。 第一引数にdone()で実行する処理、第二引数にfaile()で実行する処理を渡します。 以下の場合func1のコールバックでd.resolve()を呼んでいるので、thenの第一引数に渡した処理が実行されます。(ログにsuccessとでます。) またthen()の第二引数を省略するとdone()と同等になります。なので覚えるのはthen()だけでいい気もします。

func1 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hoge")
    d.resolve()  #resolve
  ,1000
  return d.promise()

promise = func1()
promise.then(()->
  setTimeout ()->
    console.log("success")
  , 2000
()->
  setTimeout ()->
    console.log("failed")
  , 2000
)

また、then()はメソッドチェーンでつなぐことができるので以下のような順次処理をわかりやすく記述できます。 以下のコードを実行すると1秒おきに"hoge"->"hogehoge"->"hogehogehoge"->"hogehogehogehoge"とコンソールにはきます。

func1 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hoge")
    d.resolve()  #resolve
  ,1000
  return d.promise()

func2 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hogehoge")
    d.resolve()  #resolve
  ,1000
  return d.promise()

func3 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hogehogehoge")
    d.resolve()  #resolve
  ,1000
  return d.promise()

func4 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hogehogehogehoge")
    d.resolve()  #resolve
  ,1000
  return d.promise()

promise = func1()
promise.then(func2).then(func3).then(func4)

deferred.when()

例えば完了時間がまちまち複数の非同期通信を行い、すべてのデータがそろってから処理したい場合ってありますよね。 この場合はwhen()を使うと便利なようです。以下のように各ことですべての非同期処理が完了後thenに渡した処理が実行されます。

func1 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hoge")
    d.resolve()  #resolve
  ,1000
  return d.promise()

func2 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hogehoge")
    d.resolve()  #resolve
  ,2000
  return d.promise()

func3 = ()->
  d = new $.Deferred
  setTimeout ()->
    console.log("hogehogehoge")
    d.resolve()  #resolve
  ,3000
  return d.promise()

promises = []
promises.push(func1())
promises.push(func2())
promises.push(func3())

$.when(promises...).then ()->
  console.log "すべての非同期処理が完了"

まとめ

いい!もっと早く知っとくべきでした。