Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

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

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を積極的につかってみよう!

YusukeHirao
DXデザイナー/エンジニア。マークアップ/フロントエンド/アクセシビリティが専門。
https://yusukehirao.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした