jQuery callback per più chiamate Ajax

Voglio fare tre chiamate Ajax in un evento click. Ogni chiamata Ajax esegue un’operazione distinta e restituisce i dati necessari per un callback finale. Le chiamate stesse non dipendono l’una dall’altra, possono andare tutte nello stesso momento, tuttavia mi piacerebbe avere un callback finale quando tutti e tre sono completi.

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

Ecco un object di callback che ho scritto in cui è ansible impostare un singolo callback da triggersre una volta che tutti sono completi o lasciare che ognuno abbia il proprio callback e triggersrli tutti una volta che sono stati completati:

AVVISO

Dal momento che jQuery 1.5+ è ansible utilizzare il metodo posticipato come descritto in un’altra risposta:

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

Esempio di rinviato qui

per jQuery <1.5 funzionerà o se è necessario far scattare le chiamate ajax in orari sconosciuti come mostrato qui con due pulsanti: attivato dopo aver fatto clic su entrambi i pulsanti

[uso]

per la singola richiamata una volta completata: Esempio di lavoro

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

ciascuno con il proprio callback quando tutto è completato: Esempio di lavoro

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

[Il codice]

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

Sembra che tu abbia alcune risposte a questo, tuttavia penso che ci sia qualcosa che vale la pena menzionare qui che semplificherà enormemente il tuo codice. jQuery ha introdotto $.when nella v1.5. Sembra:

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

Non l’ho visto menzionato qui, spero che aiuti.

Non vedo il bisogno di alcun object malarky me stesso. Semplice ha una variabile che è un intero. Quando si avvia una richiesta, incrementare il numero. Quando lo completi, lo decrementi. Quando è zero, non ci sono richieste in corso, quindi hai finito.

 $('#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 la pena notare che poiché $.when aspetta che tutte le richieste di ajax siano argomenti sequenziali (non un array), si vedrà comunemente $.when usato con .apply() modo:

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

Questo perché $.when accetta argomenti come questo

 $.when(ajaxRequest1, ajaxRequest2, ajaxRequest3); 

E non così:

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

EDIT – forse l’opzione migliore sarebbe quella di creare un endpoint di servizio che faccia tutto ciò che fanno le tre richieste. In questo modo devi solo fare una richiesta e tutti i dati sono dove vuoi che siano nella risposta. Se scopri che stai facendo sempre le stesse 3 richieste, probabilmente vorrai seguire questa strada. Spesso è una buona decisione di progettazione impostare un servizio di facciata sul server che rimpicciolisce azioni server di piccole dimensioni comunemente utilizzate insieme. Solo un’idea


un modo per farlo sarebbe quello di creare un object ‘sincronizzazione’ nel tuo gestore di clic prima che le chiamate ajax. Qualcosa di simile a

 var sync = { count: 0 } 

La sincronizzazione sarà associata automaticamente alla portata delle chiamate di successo (chiusura). Nel gestore di successo, si incrementa il conteggio e, se è 3, è ansible chiamare l’altra funzione.

In alternativa, potresti fare qualcosa di simile

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

quando ogni successo viene eseguito, cambierebbe il valore nella sincronizzazione in vero. Dovresti controllare la sincronizzazione per assicurarti che tutti e tre siano veri prima di procedere.

Nota il caso in cui uno dei tuoi xhrs non ritorna successo: devi renderne conto.

Un’altra opzione potrebbe essere quella di chiamare sempre la funzione finale nei gestori di successo e di avere accesso all’opzione di sincronizzazione per determinare se effettivamente fare qualcosa. Dovresti però assicurarti che la sincronizzazione rientri nell’ambito di quella funzione.

Mi piace l’idea di hvgotcodes. Il mio suggerimento è quello di aggiungere un incrementatore generico che confronta il numero completo con il numero necessario e quindi esegue il callback finale. Questo potrebbe essere incorporato nel callback finale.

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

[Modificato per riflettere gli aggiornamenti dei nomi.]

Ho fatto la stessa domanda qualche tempo fa e ho ottenuto un paio di buone risposte qui: Il modo migliore per aggiungere un ‘callback’ dopo una serie di chiamate XHR asincrone

Ho ricevuto alcuni buoni suggerimenti dalle risposte in questa pagina. L’ho adattato un po ‘per il mio uso e ho pensato di poterlo condividere.

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

Ci sono alcune limitazioni qui, ma nel mio caso era ok. Ho anche scoperto che per cose più avanzate c'è anche un plugin AOP (per jQuery) che potrebbe essere utile: http://code.google.com/p/jquery-aop/

Mi sono imbattuto in questo problema oggi, e questo è stato il mio ingenuo tentativo prima di guardare la risposta accettata.

  

Non è jquery (e sembra che jquery abbia una soluzione praticabile) ma proprio come un’altra opzione ….

Ho avuto problemi simili a lavorare pesantemente con i servizi Web di SharePoint: spesso è necessario estrarre i dati da più origini per generare input per un singolo processo.

Per risolverlo ho incorporato questo tipo di funzionalità nella mia libreria di astrazione AJAX. È ansible definire facilmente una richiesta che attiverà una serie di gestori al termine. Tuttavia ogni richiesta può essere definita con più chiamate http. Ecco il componente (e la documentazione dettagliata):

DPAJAX presso DepressedPress.com

Questo semplice esempio crea una richiesta con tre chiamate e quindi trasmette le informazioni, nell’ordine di chiamata, a un singolo gestore:

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

Si noti che a differenza di molte delle altre soluzioni (inclusa, credo, la soluzione “when” in jquery) a condizione che questo metodo non imponga il threading singolo delle chiamate effettuate – ognuna continuerà a funzionare altrettanto rapidamente (o lentamente) come l’ambiente consente ma il singolo gestore verrà chiamato solo quando tutti sono completi. Supporta anche l’impostazione dei valori di timeout e riprova i tentativi se il tuo servizio è un po ‘flakey.

L’ho trovato incredibilmente utile (e incredibilmente semplice da capire dal punto di vista del codice). Basta con il concatenare, non contare più le chiamate e risparmiare l’output. Basta “impostalo e dimenticalo”.

 async : false, 

Per impostazione predefinita, tutte le richieste vengono inviate in modo asincrono (ad esempio, questa opzione è impostata su true per impostazione predefinita). Se hai bisogno di richieste sincrone, imposta questa opzione su false . dataType: "jsonp" domini e dataType: "jsonp" richieste dataType: "jsonp" non supportano l’operazione sincrona. Si noti che le richieste sincrone possono bloccare temporaneamente il browser, disabilitando tutte le azioni mentre la richiesta è triggers. A partire da jQuery 1.8 , l’uso di async: false con jqXHR ( $.Deferred ) è deprecato; è necessario utilizzare le opzioni di callback success / error / complete invece dei metodi corrispondenti dell’object jqXHR.done() come jqXHR.done() o il jqXHR.success() obsoleto.

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

Mi dispiace, ma non posso spiegare cosa mi piace perché sono un coreano che non parla nemmeno una parola in inglese. ma penso che tu possa facilmente capirlo.