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 "すべての非同期処理が完了"
まとめ
いい!もっと早く知っとくべきでした。