結果の順番を保証して、且つ『並列』でAjax通信を行う方法

  • 208
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

複数の非同期通信をする場合

適当に作るとこんな感じ。※変数宣言などアンチパターンなのはご愛嬌

var request = [
    { url: 'hoge00.json', params: { hoge: 123, huga: 456 } },
    { url: 'hoge01.json', params: { hoge: 246, huga: 912 } },
    { url: 'hoge02.json', params: { hoge: 369, huga: 1368 } },
    { url: 'hoge03.json', params: { hoge: 492, huga: 1824 } },
    { url: 'hoge04.json', params: { hoge: 615, huga: 2280 } }
];

var results = [];
var doneCount = 0;

for (var i = 0; i <= request.length; i++) {
    $.ajax({
        url: request[i].url,
        data: request[i].params,
        type: 'POST',
        dataType: 'json',
        success: function (responce) {
            results.push(responce);
            doneCount++;
            if (doneCount === resqest.length) {
                allDone();
            }
        }
    });
}

function allDone () {
    results; // => 結果の順番は通信が成功した順番
};

配列requestに格納したURLの順番と結果の順番は同じとは限らない。

初心者がよくハマるポイントだけど、ほぼ一斉に非同期通信を行い($.ajaxメソッドを実行し)、通信が成功した順番にsuccessコールバックが実行されるので、その成功した結果の順番にresultsがプッシュされるからである。

こんな感じにできたら理想的

var request = [
    { url: 'hoge00.json', params: { hoge: 123, huga: 456 } },
    { url: 'hoge01.json', params: { hoge: 246, huga: 912 } },
    { url: 'hoge02.json', params: { hoge: 369, huga: 1368 } },
    { url: 'hoge03.json', params: { hoge: 492, huga: 1824 } },
    { url: 'hoge04.json', params: { hoge: 615, huga: 2280 } }
];

var jqXHR = $.multiAjax({
    request: request,
    type: 'POST',
    dataType: 'json'
});

jqXHR.done(function (responce) {
    responce; // => [00の結果, 01の結果, 02の結果, 03の結果, 04の結果]
});

上記を実現するには

直列処理ならば...

リクエストをひとつひとつ処理していく方法は、結構いろんなところで紹介されている。

基本的にはひとつずつAjax通信をして、そのコールバックで次のAjax通信を行なっている。もしくはasyncfalseにして、非同期通信でなく完全な同期通信をしてしまう方法。これなら結果の順番は絶対変わらない。

でも、やりたいのはそうじゃなくて、通信を同時に一気に行いたい。通信時間短縮のためには『並列』というのが大事。それに加えて結果の順番も保証してほしい。

$.whenを使えば意外と簡単に実現できる。

var request = [
    { url: 'hoge00.json', params: { hoge: 123, huga: 456 } },
    { url: 'hoge01.json', params: { hoge: 246, huga: 912 } },
    { url: 'hoge02.json', params: { hoge: 369, huga: 1368 } },
    { url: 'hoge03.json', params: { hoge: 492, huga: 1824 } },
    { url: 'hoge04.json', params: { hoge: 615, huga: 2280 } }
];

var jqXHRList = [];

for (var i = 0; i <= request.length; i++) {
    jqXHRList.push($.ajax({ // $.ajaxの戻り値を配列に格納
        url: request[i].url,
        data: request[i].params,
        type: 'POST',
        dataType: 'json'
    }));
}

// $.when関数を利用する
// $.whenは可変長引数を取るので、apllyメソッドを利用して配列で渡せるようにする
// $.whenのコンテキスト(applyの第一引数)はjQueryである必要があるので $ を渡す
$.when.apply($, jqXHRList).done(function () {
    var json = [];
    var statuses = [];
    var jqXHRResultList = [];
    // 結果は仮引数に可変長で入る **順番は保証されている**
    // 取り出すには arguments から取り出す
    // さらにそれぞれには [data, textStatus, jqXHR] の配列になっている
    for (var i = 0; i < arguments.length; i++) {
        var result = arguments[i];
        json.push(result[0]);
        statuses.push(result[1]);
        jqXHRResultList.push(result[3]);
    }

    json;// => リクエストの配列と同じ順番で結果を参照できる

});

こんなロジックで、先に記した理想に沿ったプラグインにするなど、いろいろ作ればいい。

要するに$.when.apply便利

Ajaxに限らずアニメーションなども、このテクニックを使えば並列の処理が行える。

読み込む外部ファイルやアニメーション要素の数が動的に変化する場合、$.whenのまま使うと可変長引数で渡すときに戸惑うが、applyを使えば第一引数にさえ気をつければ簡単だ。ついでに結果は順番が保証されている。

仕様上の注意

配列の中身が一個だと結果が配列でなくなるので注意

$.when.apply($, [req, req, req]).done(function () {
    arguments; // => [[data, status, jqXHR], [data, status, jqXHR], [data, status, jqXHR]]
});
$.when.apply($, [req]).done(function () {
    arguments; // => [data, status, jqXHR]
}); 

なんともありがた迷惑な仕様…。配列の個数が1以下になる可能性がある場合は適切な処置が必要。


$.when.applyを積極的につかってみよう!

  • この記事は以下の記事からリンクされています
  • Ajax備忘録からリンク