web-dev-qa-db-ja.com

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

クリックイベントで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*/});
});
123
MisterIsaak

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

通知

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

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

ここで延期の例

jQuery <1.5の場合、次のように動作します。または、ここに示すように2つのボタンを使用して不明な時間にajax呼び出しを起動する必要がある場合は、 両方のボタンをクリックした後に起動します

[使用法]

forsingleコールバックが完了したら:Working Example

// 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);
    }
});

それぞれが自分のコールバックをすべて完了したら:Working Example

//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);
        };
    };
})();
102
subhaze

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

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

ここで言及されていませんでした。

145
Greg

自分自身でオブジェクトの必要性を認識していない。シンプルな変数は整数です。リクエストを開始するときに、番号を増やします。完了したら、デクリメントします。ゼロの場合、進行中の要求はないため、完了です。

$('#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
        }
    });
});
10
Matt

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

// 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]);
9
Cory Danielson

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

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

[名前の更新を反映するように編集されました。]

4
Adolph Trudeau

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


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

var sync = {
   count: 0
}

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

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

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

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

Xhrsの1つが成功を返さない場合に注意してください-それを説明する必要があります。

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

3
hvgotcodes

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

<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
OscarRyz

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

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);
?>
0
medicalbird

わかりました、これは古いですが、解決策を提供させてください:)

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
sic

私は少し前に同じ質問をし、ここでいくつかの良い答えを得ました: 一連の非同期XHR呼び出しの後に「コールバック」を追加する最良の方法

0
SBUJOLD

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

// 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
PålOliver

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

SharePoint Webサービスとの連携で同様の問題が発生しました。1つのプロセスの入力を生成するために、複数のソースからデータをプルする必要がよくあります。

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

DepressedPress.comのDPAJAX

この簡単な例では、3つの呼び出しで1つの要求を作成し、その情報を呼び出し順に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の「when」ソリューションを含む)とは異なり、このメソッドが実行される呼び出しのシングルスレッドを強制しない場合、それぞれが環境の許す限り高速(または低速)で実行されることに注意してください。ただし、すべてが完了した場合にのみ、単一のハンドラーが呼び出されます。また、タイムアウト値の設定と、サービスが少し不安定な場合の再試行をサポートします。

私はそれがめちゃくちゃ便利だとわかりました(そしてコードの観点から理解するのは信じられないほど簡単です)。チェーン接続、コールのカウント、出力の保存はもう必要ありません。 「設定して忘れてください」だけです。

0
Jim Davis