複数のajax呼び出し用のjQueryコールバック


132

クリックイベントで3つのajax呼び出しを行います。各ajax呼び出しは個別の操作を実行し、最終的なコールバックに必要なデータを返します。呼び出し自体は互いに依存しておらず、すべて同時に実行できますが、3つすべてが完了したときに最終的なコールバックが必要です。

$('#button').click(function() {
    fun1();
    fun2();
    fun3();
//now do something else when the requests have done their 'success' callbacks.
});

var fun1= (function() {
    $.ajax({/*code*/});
});
var fun2 = (function() {
    $.ajax({/*code*/});
});
var fun3 = (function() {
    $.ajax({/*code*/});
});


回答:


112

これが私が書いたコールバックオブジェクトです。1つのコールバックをすべて完了すると起動するように設定するか、各コールバックに独自のコールバックを持たせて、すべてが完了するとそれらをすべて起動することができます。

通知

jQuery 1.5以降では、別の回答で説明されているように遅延メソッドを使用できます。

  $.when($.ajax(), [...]).then(function(results){},[...]);

ここで延期の例

jQuery <1.5の場合、次のように機能します。または、次の2つのボタンで示すように、不明な時間にajax呼び出しを発生させる必要がある場合:両方のボタンがクリックされた後に発生します

[使用法]

一度完了した単一のコールバックの場合:実例

// initialize here
var requestCallback = new MyRequestsCompleted({
    numRequest: 3,
    singleCallback: function(){
        alert( "I'm the callback");
    }
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});

それぞれが自分のコールバックをするとき、すべての完全な:実施例

//initialize 
var requestCallback = new MyRequestsCompleted({
    numRequest: 3
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the first callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the second callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the third callback');
        });
    }
});

[コード]

var MyRequestsCompleted = (function() {
    var numRequestToComplete, requestsCompleted, callBacks, singleCallBack;

    return function(options) {
        if (!options) options = {};

        numRequestToComplete = options.numRequest || 0;
        requestsCompleted = options.requestsCompleted || 0;
        callBacks = [];
        var fireCallbacks = function() {
            alert("we're all complete");
            for (var i = 0; i < callBacks.length; i++) callBacks[i]();
        };
        if (options.singleCallback) callBacks.push(options.singleCallback);

        this.addCallbackToQueue = function(isComplete, callback) {
            if (isComplete) requestsCompleted++;
            if (callback) callBacks.push(callback);
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.requestComplete = function(isComplete) {
            if (isComplete) requestsCompleted++;
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.setCallback = function(callback) {
            callBacks.push(callBack);
        };
    };
})();

1
すばらしいコード例をありがとう!残りの部分でつまずくことができると思います。再度、感謝します!
MisterIsaak

問題はありませんでした。私はそれでちょっと夢中になりました:Pにはまだいくつかのアイデアがあります。初期化時に多数のリクエストを指定する必要がない場所に更新する予定です。
subhaze

いいですね。非常に興味深いので、追加のコールバックを追加するオプションを投入することをお勧めします。私はそうしました、そしてそれは私がやりたいことに完璧に働きます。再度、感謝します!
MisterIsaak

私はしましたが、それを使用例に追加するのを忘れていました:P setCallbackをさらにキューに追加するために使用します。今はそれについて考えていますが... addCallbackキューに追加されているので、単に設定するだけではなく、それを呼び出す必要があります
subhaze

singleCallback2行目の変数は不要ですか?それとも何か不足していますか?
nimcap

158

あなたはこれに対するいくつかの答えを持っているように見えますが、私はあなたのコードを大幅に簡略化するここで言及する価値があるものがあると思います。jQuery $.whenはv1.5で導入されました。それは次のようになります:

$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
    //this callback will be fired once all ajax calls have finished.
});

ここで言及されていないので、お役に立てば幸いです。


6
答えをありがとう、これは間違いなく私のために行く方法です。jQueryを使用しており、コンパクトで短く、表現力に優れています。
nimcap 2012

2
Subhazeの答えは素晴らしいですが、実際、これは正しい答えです。
Molomby 2012

9
これはjQuery 1.5以降を使用している場合に適切なソリューションであることに同意しますが、回答時には遅延機能は使用できませんでした。:(
subhaze

13

私自身、天敵の必要性を感じていません。単純な変数は整数です。リクエストを開始するときに、番号を増やします。完了したら、デクリメントします。ゼロの場合、進行中のリクエストはないので完了です。

$('#button').click(function() {
    var inProgress = 0;

    function handleBefore() {
        inProgress++;
    };

    function handleComplete() {
        if (!--inProgress) {
            // do what's in here when all requests have completed.
        }
    };

    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
});

これは非常にシンプルで完璧に動作します... if(--inProgress)で何が起こるかをもう少し説明していただけませんか?変数inProgress == 0かどうかを制御し、それに1ユニットを減算しますが、コンパクトな構文が得られません...
Pitto

1
@ピット:おっと...実際には、そこに論理エラーがありますif (!--inProgress)。正しいバージョンはに類似していinProgress = inProgress - 1; if (inProgress == 0) { /* do stuff */ }ます。コンパクトフォームは、プレフィックスデクリメント演算子(--と否定演算子(!)を組み合わせて、ifデクリメントのinProgress結果がの場合は「true」と評価し(したがってブロックを実行)、inProgress == 0それ以外の場合は「false」と評価します(したがって何もしません)。
マット

単に素晴らしい!あなたの時間と忍耐をありがとう。
Pitto 2015

@Pitto:うーん..使用しているコードにリンクできますか?それは私のため働きます
マット

10

$.whenすべてのajaxリクエストはシーケンシャル引数(配列ではない)として期待$.whenされるため、次の.apply()ように一般的に使用されることに注意してください。

// Save all requests in an array of jqXHR objects
var requests = arrayOfThings.map(function(thing) {
    return $.ajax({
        method: 'GET',
        url: 'thing/' + thing.id
    });
});
$.when.apply(this, requests).then(function(resp1, resp2/*, ... */) {
    // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
    var responseArgsArray = Array.prototype.slice.call(this, arguments);

});

これは、$.whenこのような引数を受け入れるためです

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

そしてこれは好きではありません:

$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);

素晴らしい反応。現在、たくさんの良いpromise libがあり、それらのほとんどはallメソッドまたはそれに似たものを持っていますが、私はマップのコンセプトが好きです。いいね。
グレッグ、

2
ええ、私はほとんどの場合、リクエストの配列を適用するときに$ .whenを使用します。ここではそれをソリューションで見つけなかったので、ここに良い例を示すために追加しました。
Cory Danielson

4

hvgotcodesのアイデアが好きです。私の提案は、完了した数と必要な数を比較してから、最後のコールバックを実行する汎用インクリメンターを追加することです。これは、最終的なコールバックに組み込むことができます。

var sync = {
 callbacksToComplete = 3,
 callbacksCompleted = 0,
 addCallbackInstance = function(){
  this.callbacksCompleted++;
  if(callbacksCompleted == callbacksToComplete) {
   doFinalCallBack();
  }
 }
};

[名前の更新を反映するように編集。]


^同意し、私が実際に必要なものをよりよく理解するのに役立ちました。
MisterIsaak

3

編集-おそらく最良のオプションは、3つの要求が行うすべてを行うサービスエンドポイントを作成することです。この方法では、1つの要求を実行するだけで済み、すべてのデータは応答で必要な場所にあります。同じ3つのリクエストを何度も繰り返している場合は、おそらくこのルートを使用することをお勧めします。多くの場合、一般的に使用される小さなサーバーアクションをまとめてまとめるサーバー上にファサードサービスを設定することは、良い設計上の決定です。ただのアイデア。


それを行う1つの方法は、ajaxが呼び出される前に、クリックハンドラーに「同期」オブジェクトを作成することです。何かのようなもの

var sync = {
   count: 0
}

同期は成功呼び出しのスコープに自動的にバインドされます(閉鎖)。成功ハンドラでは、カウントをインクリメントし、3の場合は他の関数を呼び出すことができます。

または、次のようなこともできます

var sync = {
   success1Complete: false,
   ...
   success3Complete: false,
}

それぞれの成功が実行されると、同期の値がtrueに変更されます。続行する前に、3つすべてが正しいことを確認するために同期をチェックする必要があります。

xhrsの1つが成功を返さない場合に注意してください-そのことを考慮する必要があります。

さらに別のオプションは、常に成功ハンドラの最後の関数を呼び出し、同期オプションにアクセスして実際に何かを実行するかどうかを決定することです。ただし、同期がその関数のスコープ内にあることを確認する必要があります。


いくつかの理由で、私はあなたの最初の提案をより気に入っています。それぞれのリクエストには明確な目的があるので、その理由でそれらを別々に保持したいと思います。また、他の場所で使用しているため、リクエストは実際には関数内にあるため、可能であればモジュール式の設計を維持しようと試みました。ご協力いただきありがとうございます!
MisterIsaak

@Jisaak、そうです、これを処理するためにサーバーにメソッドを置くのはあなたには意味がないかもしれません。ただし、サーバー上にファサードを設定することは許容されます。あなたはモジュール性について話し、3つすべてを処理する1つのメソッドを作成することはモジュール式です。
hvgotcodesは2010


0

このページの回答からいくつかの良いヒントを得ました。私はそれを自分の用途に少し合わせて、共有できると思いました。

// lets say we have 2 ajax functions that needs to be "synchronized". 
// In other words, we want to know when both are completed.
function foo1(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo1');
            callback();               
        }
    });
}

function foo2(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo2');
            callback();
        }
    });
}

// here is my simplified solution
ajaxSynchronizer = function() {
    var funcs = [];
    var funcsCompleted = 0;
    var callback;

    this.add = function(f) {
        funcs.push(f);
    }

    this.synchronizer = function() {
        funcsCompleted++;
        if (funcsCompleted == funcs.length) {
            callback.call(this);
        }
    }

    this.callWhenFinished = function(cb) {
        callback = cb;
        for (var i = 0; i < funcs.length; i++) {
            funcs[i].call(this, this.synchronizer);
        }
    }
}

// this is the function that is called when both ajax calls are completed.
afterFunction = function() {
    alert('All done!');
}

// this is how you set it up
var synchronizer = new ajaxSynchronizer();
synchronizer.add(foo1);
synchronizer.add(foo2);
synchronizer.callWhenFinished(afterFunction);

ここにはいくつかの制限がありますが、私の場合は問題ありませんでした。私はまた、より高度なもののために、役に立つかもしれないAOPプラグイン(jQuery用)があることも発見しました:http : //code.google.com/p/jquery-aop/


0

今日私はこの問題に遭遇しましたが、これは受け入れられた回答を見る前の私の素朴な試みでした。

<script>
    function main() {
        var a, b, c
        var one = function() {
            if ( a != undefined  && b != undefined && c != undefined ) {
                alert("Ok")
            } else {
                alert( "¬¬ ")
            }
        }

        fakeAjaxCall( function() {
            a = "two"
            one()
        } )
        fakeAjaxCall( function() {
            b = "three"
            one()
        } )
        fakeAjaxCall( function() {
            c = "four"
            one()
        } )
    }
    function fakeAjaxCall( a ) {
        a()
    }
    main()
</script>

0

それはjqueryではありません(そしてjqueryには実行可能な解決策があるようです)が、別のオプションとして...

SharePoint Webサービスで頻繁に機能する同様の問題がありました。単一のプロセスの入力を生成するには、複数のソースからデータをプルする必要があることがよくあります。

それを解決するために、この種の機能をAJAX抽象化ライブラリーに組み込みました。完了時に一連のハンドラーをトリガーする要求を簡単に定義できます。ただし、各リクエストは複数のhttp呼び出しで定義できます。コンポーネント(および詳細なドキュメント)は次のとおりです。

DepressedPress.comのDPAJAX

この簡単な例では、3つの呼び出しで1つのリクエストを作成し、その情報を呼び出し順に単一のハンドラーに渡します。

    // The handler function 
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) }; 

    // Create the pool 
myPool = DP_AJAX.createPool(); 

    // Create the request 
myRequest = DP_AJAX.createRequest(AddUp); 

    // Add the calls to the request 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]); 

    // Add the request to the pool 
myPool.addRequest(myRequest); 

他の多くのソリューションとは異なり(jqueryの「いつ」のソリューションを含むと考えられます)、このメソッドは行われる呼び出しのシングルスレッド化を強制しないことに注意してください-それぞれが環境が許す限り速く(または遅く)実行されますただし、単一のハンドラーは、すべてが完了したときにのみ呼び出されます。また、タイムアウト値の設定と、サービスが不安定な場合の再試行もサポートしています。

私はそれがめちゃくちゃ便利(そしてコードの観点から理解するのは信じられないほど簡単)だとわかりました。連鎖、呼び出しのカウント、出力の保存は不要です。「設定して忘れる」だけです。


0

これは古いですが、私の解決策を寄稿させてください:)

function sync( callback ){
    syncCount--;
    if ( syncCount < 1 ) callback();
}
function allFinished(){ .............. }

window.syncCount = 2;

$.ajax({
    url: 'url',
    success: function(data) {
        sync( allFinished );
    }
});
someFunctionWithCallback( function(){ sync( allFinished ); } )

コールバックを持つ関数でも動作します。syncCountを設定し、すべてのアクションのコールバックで関数sync(...)を呼び出します。


0

キューを配置する追加のメソッドを必要とせずにそれを行う簡単な方法を見つけました。

JS

$.ajax({
  type: 'POST',
  url: 'ajax1.php',
  data:{
    id: 1,
    cb:'method1'//declaration of callback method of ajax1.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method1'
  eval(cb+"(JSON.stringify(data));");//here we calling method1 and pass all data
  }
});


$.ajax({
  type: 'POST',
  url: 'ajax2.php',
  data:{
    id: 2,
    cb:'method2'//declaration of callback method of ajax2.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method2'
  eval(cb+"(JSON.stringify(data));");//here we calling method2 and pass all data
  }
});


//the callback methods
function method1(data){
//here we have our data from ajax1.php
alert("method1 called with data="+data);
//doing stuff we would only do in method1
//..
}

function method2(data){
//here we have our data from ajax2.php
alert("method2 called with data="+data);
//doing stuff we would only do in method2
//..
}

PHP(ajax1.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method1'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax1"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

PHP(ajax2.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method2'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax2"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

-1
async   : false,

デフォルトでは、すべてのリクエストは非同期に送信されます(つまり、デフォルトではtrueに設定されています)。同期リクエストが必要な場合は、このオプションをに設定してくださいfalse。クロスドメイン要求とdataType: "jsonp"要求は、同期操作をサポートしていません。同期リクエストは一時的にブラウザをロックし、リクエストがアクティブな間はアクションを無効にする場合があることに注意してください。以下のようにjQueryの1.8の使用async: falsejqXHRは$.Deferred)は推奨されません。または非推奨のようなjqXHRオブジェクトの対応するメソッドの代わりに成功/エラー/完了のコールバックオプションを使用する必要があります。jqXHR.done()jqXHR.success()


これは適切な解決策ではありません。同期Ajaxリクエストは決して行わないでください。
user128216 2016年

-2
$.ajax({type:'POST', url:'www.naver.com', dataType:'text', async:false,
    complete:function(xhr, textStatus){},
    error:function(xhr, textStatus){},
    success:function( data ){
        $.ajax({type:'POST', 
            ....
            ....
            success:function(data){
                $.ajax({type:'POST',
                    ....
                    ....
            }
        }
    });

申し訳ありませんが、何が問題だったのか説明できません。私は韓国語で、英語の単語も話せません。簡単に理解できると思います。


このようにajaxを使用しないでください。コールバック地獄..!
traeper 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.