devolución de llamada jQuery para múltiples llamadas ajax
Quiero hacer tres llamadas ajax en un evento de clic. Cada llamada ajax realiza una operación distinta y devuelve los datos necesarios para una devolución de llamada final. Las llamadas en sí no dependen una de otra, todas pueden realizarse al mismo tiempo, sin embargo, me gustaría tener una devolución de llamada final cuando las tres estén completas.
$('#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*/});
});
Parece que tienes algunas respuestas a esto, sin embargo, creo que hay algo que vale la pena mencionar aquí y que simplificará enormemente tu código. jQuery introdujo el $.when
en v1.5. Parece que:
$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
//this callback will be fired once all ajax calls have finished.
});
No lo vi mencionado aquí.
Aquí hay un objeto de devolución de llamada que escribí donde puedes configurar una única devolución de llamada para que se active una vez que se haya completado o dejar que cada uno tenga su propia devolución de llamada y que se activen todas una vez que se hayan completado:
AVISO
Desde jQuery 1.5+ puedes usar el método diferido como se describe en otra respuesta:
$.when($.ajax(), [...]).then(function(results){},[...]);
Ejemplo de diferido aquí
para jQuery < 1.5 lo siguiente funcionará o si necesita que sus llamadas ajax se activen en momentos desconocidos como se muestra aquí con dos botones: se activan después de hacer clic en ambos botones
[uso]
para devolución de llamada única una vez completada: Ejemplo de trabajo
// 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 uno tiene su propia devolución de llamada cuando todo está completo: Ejemplo de trabajo
//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');
});
}
});
[El 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);
};
};
})();
No veo la necesidad de ningún objeto. Simple tiene una variable que es un número entero. Cuando inicie una solicitud, incremente el número. Cuando uno se complete, disminuya. Cuando es cero, no hay solicitudes en curso, así que ya está.
$('#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 señalar que, dado que $.when
espera que todas las solicitudes ajax sean argumentos secuenciales (no una matriz), comúnmente verá que $.when
se usan .apply()
así:
// 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);
});
Usando la sintaxis Spread , ahora puedes escribir este código así:
$.when(...requests).then((...responses) => {
// do something with responses
})
Esto se debe a que $.when
acepta argumentos como este.
$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);
Y no así:
$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);