一から勉強させてください( ̄ω ̄;)

最下級エンジニアが日々の学びをアウトプットしていくだけのブログです。

jQuery.DeferredとかjQuery.whenの使い方について

今回はjQuery.Deferredとかそれに関連するjQuery.whenとかのことをざっくり書こうと思います。


まずざっくりとDeferredについて。
Deferredオブジェクトはunresolved, resolved, rejectedのいずれかの状態をもつオブジェクトです(デフォルト値はunresoleved)。


んで、

・unresolved状態→後続の処理は実行されない。
・resolved状態→resolvedになった時用の処理(要するにdone)が実行される。
・rejected状態→rejectedになった時用の処理(要するにfail)が実行される。


といった具合に、状態に応じて後続の処理を管理してくれます。
(あ、ちなみにここ地味に重要なんですが、一度状態が変わったDeferredオブジェクトはもう二度と別の状態には変わりません。役目終了です。)


これによって、実行の順番を管理するのが難しい非同期処理においても、


・非同期処理Aが終わったら、次に非同期処理Bを実行してどっちも終わったら、A、Bの返り値を使って処理Cを行う


みたいなことが簡単にできます。


まず、簡単なサンプルを書いてみました。

function hoge(){
    var dfd = $.Deferred();
    setTimeout(function(){
        console.log("hoge");
        dfd.resolve();
    }, 2000);
    
    return dfd.promise();
}

function fuga(){
    console.log("fuga");
}

hoge().done(fuga); //2秒後、hoge, fugaの順に表示される


こんな具合で非同期な処理の管理も自由自在にできるんですね。便利!!


ちなみにpromise()というメソッドがでてきましたが、これはPromiseオブジェクトというDeferredオブジェクトからresolve(), reject()の機能を削除したオブジェクトを返します。
簡単にいうと「まだunresolvedの状態をキープしとけよ。許可無く勝手に状態返んなよ。」って感じですかね。


んで次にresolve()またはreject()して、状態をresolvedまたはrejectedに変更し、その後、その状態に応じた後続処理を実行するといった流れになります。

done()の中身がresolved状態で実行され、fail()の中身がrejected状態で実行されます。
上記サンプルではdoneしか書いてないです、、


ちなみにthen(func1, func2)とすると、func1がresolvedでの処理、func2がrejectedでの処理といった具合に両方の処理を入れる事もできますよ!


さて。では次にDeferredの仕組みを使ったスーパー便利メソッド「$.when()」について。
これはさきほど書いた、


・非同期処理Aが終わったら、次に非同期処理Bを実行してどっちも終わったら、A、Bの返り値を使って処理Cを行う


みたいなことを実現するためのメソッドになります。

whenを使えば、複数の非同期処理がすべて完了するまで後続の処理を遅延させる事ができます。


サンプルを書いてみました。

var hoge = $.ajax({
    url : "~" ,//{"text" : "hoge"}を返すAPIとする
    type : "GET",
    dataType : "json"
});

var fuga = $.ajax({
    url : "~" ,//{"text" : "fuga"}を返すAPIとする
    type : "GET",
    dataType : "json"
});

$.when(hoge, fuga).done(function(data1, data2){
    console.log(data1[0].text); //hoge
    console.log(data2[0].text); //fuga
});


こんな感じで複数の非同期を簡単に管理する事ができます。

ここでふたつの非同期処理hoge, fugaが終わった後のdone()の中でdata1, data2という引数がありますが、ここには、


data1→[hogeAPIから返ってくるjson, "success", jqXHR]
data2→[fugaのAPIから返ってくるjson, "success" , jqXHR]


が格納されています。配列です。
一番目にそれぞれの非同期処理から返ってくるデータ、二番目にはstatusText(今回はsuccess)、三番目にはjqXHR
が入っていますね。


ん?jqXHR?Promiseじゃねえの?


そうなんです。今回はサンプルでajaxメソッドを使ったため、厳密にはPromiseオブジェクトではなく、jqXHRというjQuery独自が用意したXMLHttpRequestオブジェクトとPromiseオブジェクトのインターフェースを併せ持つオブジェクトを返しているんです。


まあXHRとPromiseの両方の力を併せ持つ優れものだと思っておけばOKなんじゃないでしょうか。どっちのメソッドも使えます。だから当然doneとかfailもできます。


こんな感じでwhenを使えば非同期処理の制御も楽々で便利!!


あ、でも注意点(というか僕が以前ハマっただけですが)がありまして、

function hoge(){
    $.ajax({
        url : "~" ,//{"text" : "hoge"}を返すAPIとする
        type : "GET",
        dataType : "json"
    });
}

function fuga(){
    $.ajax({
        url : "~" ,//{"text" : "fuga"}を返すAPIとする
        type : "GET",
        dataType : "json"
    });
}

$.when(hoge(), fuga()).done(function(data1, data2){
    console.log(data1[0].text); //うまくいかない 
    console.log(data2[0].text);//うまくいかない
});

みたいにやらないように注意してください。

これ、functionの中にajaxの処理いれちゃうとreturn がjqXHRではなくundefinedになってしまって、うまく動作しません。


functionにいれたい場合は

function hoge(){
    return $.ajax({
        url : "~" ,//{"text" : "hoge"}を返すAPIとする
        type : "GET",
        dataType : "json"
    });
}

function fuga(){
    return $.ajax({
        url : "~" ,//{"text" : "fuga"}を返すAPIとする
        type : "GET",
        dataType : "json"
    });
}

$.when(hoge(), fuga()).done(function(data1, data2){
    console.log(data1[0].text); //hoge
    console.log(data2[0].text); //fuga
});


みたいにパワープレイでjqXHRを返してやればOKです。
あまり美しくないかもですが、、


こんな感じで便利なjQueryのDeferredの仕組み。ぜひ積極的に使ってみてください。


小さな事からコツコツと。