【ワレコの講座】jQueryの$.whenや$.deferredを使って非同期処理完了を待つ

この記事は約10分で読めます。
スポンサーリンク

JavaScriptやjQueryを使ってWEBプログラミングをやっていると、非同期処理を実行してそれが完了した時点で次の処理を行いたい場合はよくある。

例えば、Ajaxで他のサイトから何らかのデータをGETするなど。

具体的には、

  • データベースからデータを読み込む。
  • 公開されているAPIを使って何らかのデータをJSONやXML形式で取得

などか。

この記事ではJavaScriptでPromiseを使って非同期処理の完了を待つサンプルコードを書いてみた。

その後のワテはPromiseをよく使っているのだが、jQueryでも

$.when

$.deferred

という便利な関数が用意されているので、この記事ではそれらの使い方を備忘録としてまとめた。

類似の解説記事はネット上に多数あるが、この記事ではそれらの解説記事ではあまり触れられていない引数の受け渡しについても詳しく調べた。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
スポンサーリンク
スポンサーリンク

何をどうしたいのか?

例えば二つの非同期処理を実行する場合には、何も考慮せずに実行すると非同期処理の実行が完了したかどうかにかかわらずに同期処理はドンドン進んでいく(下図)。

これだと非同期処理の完了を待たずに処理が進んでしまう

この例では、同期関数1から二つの非同期関数を実行した直後に、処理の流れは同期関数2に移動する。

従って、もし同期関数2が非同期関数の処理結果を利用したい場合にはこれでは上手く行かない。

 

非同期関数の処理完了を待つ

現実的な状況では、非同期処理の完了を待ってその処理結果を受け取ってから次の同期処理2に進みたい場合が多い(下図)。

 

 

非同期処理完了を待つのはjQuery.when() と jQuery.deferred() で簡単に書ける

早速書いてみた。

この例では、二つの非同期関数

  • first()          2秒後に処理完了する非同期関数
  • second()     3秒後に処理完了する非同期関数

を実行する。

それぞれに引数も渡している。

この二つの非同期関数が完了した時点で、それらの処理結果データを受け取るdoneFunc(data1, data2)関数を実行する例を下図に示す。

$(function () {
    $.when(first('引数1'), second(2)).done(doneFunc);
});

function first(arg) {
    return $.Deferred(function () {
        var self = this;
 
        //-- 皆さんが応用する場合には、このブロックに非同期処理を書く。
        //   この例では2秒後にdata1オブジェクトデータが得られると言う例。
        setTimeout(function () {
            var data1 = { func: 'first', data: '引数は[' + arg + ']だった。' };
            self.resolve(data1); // 非同期処理が成功した場合はresolveをリターン。引数で戻り値を渡せる。
        }, 2000);
        //--
 
    });
}
 
function second(arg) {
    return $.Deferred(function () {
        var self = this;
        setTimeout(function () {
            var data2 = { func: 'second', data: '引数は[' + arg + ']だった。' }; // dataはもちろん文字列以外でも良い。
            self.resolve(data2);
        }, 3000);
    });
}
 
function doneFunc(data1, data2) {
    var result = '[doneFunc]' + JSON.stringify(data1) + ':' + JSON.stringify(data2);
    console.log(result);
    alert(result);
}

まあ、ワテも覚えたばかりの$.whenや$.deferredを即席で利用したみたので、勘違いもあるかもしれないが、一応問題なく動いている。

処理の説明

この例では $.deferredというオブジェクトをreturnする形式にして、その中で非同期関数を実行している。

二つの非同期関数firstとsecondでは処理が終わったらそれぞれ

self.resolve(data1);

self.resolve(data2);

を実行している。

その結果、最後に完了したsecond()からのデータを受け取った時点で、done()に登録されている関数、

doneFunc(data1, data2) 

が実行出来る。

これによって、二つの非同期関数が確実に完了している事が保証される。

また、非同期関数での処理結果データを受け取る事が出来るので、それを利用して次の処理を行う事が可能だ。

もしエラーした場合の処理は?

 

まあ、その場合には、resolveで返す data1やdata2 の中にエラーした事を伝えるメッセージを入れておいてdoneFuncで受信したあとでそのメッセージに応じてエラー処理をすると良いと思う。

でも、専用の .fail() が用意されているので、もしそれを使いたいならば、こんな関数を追加しておいて、

function errFunc(data: string) {
    alert('[errFunc]' + data);
}

こんな形式で実行すれば良い。

$.when(first(), second()).done(doneFunc).fail(errFunc);

かつ非同期処理ではエラーした場合に reject をリターンする。

setTimeout(function () {                  // ここで非同期(async)な処理を実行する。
 // self.resolve(data1);                  // 非同期処理が成功した場合はresolveをリターン。引数で戻り値を渡せる。
    self.reject('firstでエラーした');     // 非同期処理が失敗した場合はreject をリターン。引数で戻り値を渡せる。
}, 2000);   

これによって、非同期処理が終わったら doneFunc ではなくて errFunc がコールされるのでその中でエラー処理をすると良い。

一つの非同期処理が終わるまで待って終わったら次の処理を行う

さて、今までの例では二つの非同期処理を実行して $.when() でそれらの処理の完了を待つ例だった。

その中ではこんな形式で関数を定義したが、

function first(arg) {
    return $.Deferred(function () {
        ・・・ 
    });
}

ワテの場合、こういう形式は今一つ好きではない。何というかその行き成りreturnする記述方法が個人的には好きではない。まあ、好き好きだと思うが。

そう言う記述方法ではなくて、普通に書いてみたのが次の例だ。

次に示す例では、一つの非同期処理が終わるまで待って終わったら次の処理を行うと言う簡単な例だ。

全HTMLを掲載してみた。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>ワレコのJavaScript講座</title>
    <script src="//code.jquery.com/jquery-3.1.1.js"></script>
    <!--<script src="file1.js"></script>-->
</head>
<body>
     <script>
         test2();
  
        function test2() {
 
            var df = $.Deferred(
 
                function () {
                    var self = this;
 
                    setTimeout(function () {
                        var mydata = { arg1: 111, arg2: '非同期処理の結果データ' };
                        self.resolve(mydata);
                        //self.reject('エラーした。');
                    }, 2000);
                 }
             );
  
            df.done(function (mydata) {
                 alert('[done(成功)] mydata = [' + JSON.stringify(mydata) + ']');
             }).fail(function (mydata) {
                 alert('[fail(失敗)] mydata = [' + JSON.stringify(mydata) + ']');
             }).always(function (mydata) {
                 alert('[always(必ず最後に実行)] mydata = [' + JSON.stringify(mydata) + ']');
             });
         } 
    </script>
 </body>
</html>

もしご自身でも試してみたい人は、上のHTMLの全体をコピペして test.html などのファイル名で保存すると良いでしょう。

その場合には、文字コードは UTF-8 BOM有りにすると良いです。

他のコードなどでは文字化けする場合があります。

かつ、ファイルの冒頭で jquery-3.1.1.js をjQueryのサイトから読み込んでいますが、最近のブラウザーではセキュリティ制約があり、その部分が実行出来ません。

ですので、事前に 

http://code.jquery.com/jquery-3.1.1.js

からダウンロードしておいて、test.html ファイルと同じフォルダに保管しておきます。

次に、そのローカルに保存した jQuery を使うために以下のように書き換えます。

<!--<script src="//code.jquery.com/jquery-3.1.1.js"></script>-->
 <script src="jquery-3.1.1.js"></script>

これで準備完了です。

あとは、その text.html ファイルをダブルクリックするなどでブラウザーで開くと実行出来ます。

無事にalert文が表示されれば成功です。

エラーした場合の実験をするなら、

// self.reject('エラーした。');

の部分を有効化すると良いでしょう。

まとめ

jQueryで非同期処理を手軽に分かり易く実行出来る手法を紹介した。

$.when() と $.deferred() を使うと複数の非同期処理が全部完了した時点で、次の処理を実行出来る。

非同期処理が成功した場合には resolveをコールすると .done() で指定した関数をコール出来る。

一方、どれかの非同期処理がエラーした場合には rejectをコールする事によって .fail() で指定した関数をコール出来る。

もし成功でも失敗でも兎に角最後に何かの処理を入れたい場合には、.always() を追加しても良い。

と言う事で、今回の記事は終わり。

jQueryの本を読む

どれも人気の高い本だ。

特に二番目に紹介している本は、山田 祥寛さんの著作だ。

山田さんは、WEBプログラミング系だけではなくVisual Studio関連の解説本などでも有名だ。

アマゾンのレビューでも人気が高いのでお勧めだ。

 

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
コメント募集

この記事に関して何か質問とか補足など有りましたら、このページ下部にあるコメント欄からお知らせ下さい。

JavaScriptjQuery
スポンサーリンク
warekoをフォローする
スポンサーリンク
われこ われこ

コメント