callback jQuery para várias chamadas ajax

Eu quero fazer três chamadas ajax em um evento de clique. Cada chamada ajax faz uma operação distinta e retorna os dados necessários para um retorno de chamada final. As chamadas em si não são dependentes umas das outras, elas podem ir todas ao mesmo tempo, no entanto, eu gostaria de ter um retorno de chamada final quando todos os três estiverem completos.

$('#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*/}); }); 

    Aqui está um object de retorno de chamada que eu escrevi, onde você pode definir um único retorno de chamada para triggersr uma vez concluído ou permitir que cada um tenha seu próprio retorno de chamada e triggersr todos eles depois de concluídos:

    AVISO PRÉVIO

    Desde o jQuery 1.5+ você pode usar o método diferido conforme descrito em outra resposta:

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

    Exemplo de diferido aqui

    para jQuery <1.5, o seguinte funcionará ou se você precisar que suas chamadas ajax sejam acionadas em horários desconhecidos, conforme mostrado aqui, com dois botões: acionados após os dois botões serem clicados

    [uso]

    para retorno de chamada único depois de concluído: Exemplo de trabalho

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

    cada um tendo seu próprio retorno de chamada quando tudo estiver completo: Exemplo de trabalho

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

    [O código]

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

    Parece que você tem algumas respostas para isso, mas acho que há algo que vale a pena mencionar aqui que simplificará muito o seu código. jQuery introduziu o $.when na v1.5. Parece que:

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

    Não vi mencionado aqui, espero que ajude.

    Não vendo a necessidade de qualquer object malarky eu mesmo. Simples tem uma variável que é um inteiro. Quando você inicia uma solicitação, incremente o número. Quando um terminar, diminua-o. Quando é zero, não há solicitações em andamento, então você está pronto.

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

    Vale a pena notar que desde $.when espera todos os pedidos de ajax como argumentos seqüenciais (não uma matriz) você normalmente verá $.when .apply() usado com .apply() forma:

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

    Isso ocorre porque $.when aceita args como este

     $.when(ajaxRequest1, ajaxRequest2, ajaxRequest3); 

    E não assim:

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

    EDIT – talvez a melhor opção seja criar um terminal de serviço que faça tudo o que as três solicitações fazem. Dessa forma, você só precisa fazer uma solicitação, e todos os dados estão onde você precisa estar na resposta. Se você achar que está fazendo os mesmos 3 pedidos repetidas vezes, provavelmente desejará seguir esse caminho. Muitas vezes, é uma boa decisão de design configurar um serviço de fachada no servidor que agrega ações de servidor menores comumente usadas juntas. Apenas uma ideia.


    Uma maneira de fazer isso seria criar um object ‘sync’ no seu manipulador de cliques antes das chamadas do ajax. Algo como

     var sync = { count: 0 } 

    A synchronization será vinculada ao escopo das chamadas de sucesso automaticamente (fechamento). No manipulador de sucesso, você incrementa a contagem e, se for 3, pode chamar a outra function.

    Alternativamente, você poderia fazer algo como

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

    quando cada sucesso é executado, alteraria o valor na synchronization para true. Você teria que verificar a synchronization para se certificar de que todos os três são verdadeiros antes de prosseguir.

    Observe o caso em que um dos seus xhrs não retorna sucesso – você precisa explicar isso.

    Ainda outra opção seria sempre chamar a function final em seus manipuladores de sucesso, e ter access a opção de synchronization para determinar se realmente fazer alguma coisa. Você precisaria ter certeza de que a synchronization está no escopo dessa function.

    Eu gosto da ideia dos hvgotcodes. Minha sugestão é adicionar um incrementador genérico que compara o número completo ao número necessário e, em seguida, executa o retorno de chamada final. Isso pode ser construído no retorno de chamada final.

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

    [Editado para refletir as atualizações de nome.]

    Fiz a mesma pergunta há algum tempo e recebi algumas boas respostas aqui: A melhor maneira de adicionar um “retorno de chamada” após uma série de chamadas XHR assíncronas

    Eu tenho algumas boas dicas das respostas nesta página. Eu adaptei um pouco para o meu uso e pensei que poderia compartilhar.

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

    Existem algumas limitações aqui, mas no meu caso foi ok. Eu também descobri que para coisas mais avançadas também existe um plugin AOP (para jQuery) que pode ser útil: http://code.google.com/p/jquery-aop/

    Eu me deparei com esse problema hoje, e esta foi minha tentativa ingênua antes de assistir a resposta aceita.

      

    Não é jquery (e parece jquery tem uma solução viável), mas apenas como outra opção ….

    Eu tive problemas semelhantes trabalhando intensamente com os serviços da web do SharePoint – você geralmente precisa extrair dados de várias fonts para gerar inputs para um único processo.

    Para resolvê-lo, integrei esse tipo de funcionalidade à minha biblioteca de abstração AJAX. Você pode definir facilmente uma solicitação que acionará um conjunto de manipuladores quando concluída. No entanto, cada solicitação pode ser definida com várias chamadas http. Aqui está o componente (e documentação detalhada):

    DPAJAX na DepressedPress.com

    Este exemplo simples cria uma solicitação com três chamadas e, em seguida, passa essa informação, na ordem de chamada, para um único manipulador:

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

    Observe que, diferentemente de muitas outras soluções (inclusive, acredito que a solução “when” no jquery) forneceu esse método não força o thread único das chamadas que estão sendo feitas – cada uma ainda será executada tão rapidamente (ou tão lentamente) quanto o ambiente permitir mas o manipulador único só será chamado quando todos estiverem completos. Ele também suporta a configuração de valores de tempo limite e tentativas de repetição se seu serviço for um pouco chato.

    Eu achei insanamente útil (e incrivelmente simples de entender de uma perspectiva de código). Não há mais encadeamento, não mais contando chamadas e salvando a saída. Basta configurá-lo e esquecê-lo”.

     async : false, 

    Por padrão, todas as solicitações são enviadas de forma assíncrona (ou seja, isso é definido como true por padrão). Se você precisar de solicitações síncronas, defina essa opção como false . Solicitações de domínio cruzado e solicitações de tipo de dados dataType: "jsonp" não suportam a operação síncrona. Observe que as solicitações síncronas podem bloquear temporariamente o navegador, desativando todas as ações enquanto a solicitação estiver ativa. A partir do jQuery 1.8 , o uso de async: false com jqXHR ( $.Deferred ) foi preterido; você deve usar as opções success / error / complete callback em vez dos methods correspondentes do object jqXHR , como jqXHR.done() ou o obsoleto jqXHR.success() .

     $.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', .... .... } } }); 

    Me desculpe, mas eu não posso explicar o que eu quero, porque eu sou um coreano que não fala nem uma palavra em inglês. mas acho que você pode entender isso facilmente.