jQuery.when() で、複数の非同期処理を扱う

jQuery で AJAXするのに Deferred が便利ですよねーという話。
今回は jQuery.when() についてです。
複数の非同期処理を扱うとき
たとえば、
- JSONデータA を AJAX で取得したい
 - 別のデータB も AJAX で取得したい
 - 両方の取得が終わったら、続く処理をしたい
 
こういう処理を Deferred の仕組みを使わないで書くと、こんな感じになりますでしょうか。
var data_a, data_b;
$.getJSON('a.json', function (data_a) {
    $.getJSON('b.json', function (data_b) {
        // 両方終わった時の処理
        console.log(data_a, data_b);
    });
});
Deferred を使わないと、コールバックの中に次のコールバックを書いて…… といった書き方です。まあまだ2段階くらいならいいんですが、処理が増えるにつれ、どんどんコードの階層が深くなってしまいます。
また、これ、データA 取得に5秒、データB 取得に5秒かかるとしたら、トータルで10秒かかってしまいますね。1つの処理が終わってから、次の処理に入るので。
できれば、非同期処理を順番に実行するんじゃなくて、並列に扱いたい。データAもデータBもいっぺんに取ってきてほしい。
jQuery の when() を使う
ということで jQuery.when() を使ってみた例です。
「複数の非同期処理が全部終わったら、続く処理を行う」というのがすっきり書けます。
$.when(
    $.getJSON('a.json'),
    $.getJSON('b.json')
)
.done(function(data_a, data_b) {
    // すべて成功した時の処理
    console.log(data_a, data_b);
})
.fail(function() {
    // エラーがあった時
    console.log('error');
});
こんな感じ。
when() の中には、いくつでも Deferred な処理を書けます。書いた処理がすべて成功したら done() に処理が渡り、どれか1つでもエラーになると fail() に処理が渡ります。
コールバックを使ったものよりもコードが見やすいですし、この先なにか機能追加等で起きる変更にも強そうな構造です。
他の Deferredな処理と組み合わせたサンプル
別のサンプルをひとつ。たとえば、
「JSONデータを取得してきたい、ただし、最低でも1秒間待たせてから、次の処理をしたい」
という場合のコード。
getJSON() と、「setTimeout() でn秒経過したら promise を返す deferred」とを組み合わせます。
when() からその2つを実行させ、両方とも終了したら done() に処理が渡る、という流れ。
// 指定された秒数だけ待つ deferred
function wait(sec) {
    var d = $.Deferred();
    setTimeout(function() {
        d.resolve();
    }, sec * 1000);
    return d.promise();
}
$.when(
    $.getJSON('sample.json'),
    wait(1) // 1秒待ってから成功を返す deferred
)
.done(function(data) {
    console.log(data);
});
こんな感じでしょうかー
参考
jQuery オフィシャルのマニュアル
jQuery.when() | jQuery API Documentation
http://api.jquery.com/jQuery.when/
Yahoo! Tech Blog のこちらの記事、Deferred 関連について めちゃくちゃ分かりやすかったです。
爆速でわかるjQuery.Deferred超入門 - Yahoo! JAPAN Tech Blog
http://techblog.yahoo.co.jp/programming/jquery-deferred/


