¿Cómo hago que una función espere hasta que todas las solicitudes de jQuery Ajax se realicen dentro de otra función?
En resumen, necesito esperar a que se realicen todas las solicitudes de Ajax antes de ejecutar la siguiente. ¿Pero cómo?
¿Cómo hago que una función espere hasta que todas las solicitudes de jQuery Ajax se realicen dentro de otra función?
En resumen, necesito esperar a que se realicen todas las solicitudes de Ajax antes de ejecutar la siguiente. ¿Pero cómo?
Respuestas:
jQuery ahora define una función when para este propósito.
Acepta cualquier cantidad de objetos diferidos como argumentos y ejecuta una función cuando todos se resuelven.
Eso significa que, si desea iniciar (por ejemplo) cuatro solicitudes de ajax, luego realizar una acción cuando terminen, podría hacer algo como esto:
$.when(ajax1(), ajax2(), ajax3(), ajax4()).done(function(a1, a2, a3, a4){
// the code here will be executed when all four ajax requests resolve.
// a1, a2, a3 and a4 are lists of length 3 containing the response text,
// status, and jqXHR object for each of the four ajax calls respectively.
});
function ajax1() {
// NOTE: This function must return the value
// from calling the $.ajax() method.
return $.ajax({
url: "someUrl",
dataType: "json",
data: yourJsonData,
...
});
}
En mi opinión, crea una sintaxis limpia y clara, y evita la participación de variables globales como ajaxStart y ajaxStop, que podrían tener efectos secundarios no deseados a medida que se desarrolla su página.
Si no sabe de antemano cuántos argumentos de ajax necesita esperar (es decir, si desea usar un número variable de argumentos), aún puede hacerse, pero es un poco más complicado. Consulte Pasar en una matriz de diferidos a $ .when () (y tal vez jQuery. Al solucionar problemas con un número variable de argumentos ).
Si necesita un control más profundo sobre los modos de falla de los scripts ajax, etc., puede guardar el objeto devuelto por .when()
: es un objeto jQuery Promise que abarca todas las consultas ajax originales. Puede invocarlo .then()
o .fail()
agregarlo para agregar controladores detallados de éxito / falla.
$.when
devuelve un Promise
objeto que tiene métodos más útiles, no solo .done
. Por ejemplo, con el .then(onSuccess, onFailure)
método podría reaccionar cuando ambas solicitudes tengan éxito o al menos una de ellas falle.
fail
caso. A diferencia done
, fail
dispara inmediatamente en el primer fallo e ignora los aplazamientos restantes.
onFailure
se puede adjuntar una función. Como señalé en un comentario a la pregunta del OP: podría querer indicar con mayor precisión lo que quería decir con "hecho". "Ryan Mohr" también tuvo un muy buen punto con respecto al hecho de que se fail
comporta de manera diferente done
, ya Promises
que creo que hay que leer más sobre html5rocks.com/en/tutorials/es6/promises
Si desea saber cuándo ajax
finalizan todas las solicitudes en su documento, no importa cuántas existan, simplemente use el evento $ .ajaxStop de esta manera:
$(document).ajaxStop(function () {
// 0 === $.active
});
En este caso, no necesita adivinar cuántas solicitudes están ocurriendo en la aplicación, que podrían terminar en el futuro, ni profundizar en la lógica compleja de las funciones o encontrar qué funciones están haciendo
HTTP(S)
solicitudes.
$.ajaxStop
aquí también puede vincularse a cualquierHTML
nodo que creas que podría ser modificado por requst.
Actualización:
si desea seguir con la ES
sintaxis, puede usar Promise.all para ajax
métodos conocidos :
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Un punto interesante aquí es que funciona con Promises
y con $.ajax
solicitudes.
Aquí está la demostración de jsFiddle .
Actualización 2:
Sin embargo, una versión más reciente que utiliza la sintaxis async / await :
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
He encontrado una buena respuesta por gnarf mi mismo que es exactamente lo que estaba buscando :)
jQuery ajaxQueue
//This handles the queues
(function($) {
var ajaxQueue = $({});
$.ajaxQueue = function(ajaxOpts) {
var oldComplete = ajaxOpts.complete;
ajaxQueue.queue(function(next) {
ajaxOpts.complete = function() {
if (oldComplete) oldComplete.apply(this, arguments);
next();
};
$.ajax(ajaxOpts);
});
};
})(jQuery);
Luego puede agregar una solicitud ajax a la cola de esta manera:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Usa el ajaxStop
evento.
Por ejemplo, supongamos que tiene un mensaje de carga ... mientras recupera 100 solicitudes ajax y desea ocultar ese mensaje una vez cargado.
Del documento jQuery :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Tenga en cuenta que esperará a que se realicen todas las solicitudes de ajax en esa página.
NOTA: Las respuestas anteriores utilizan una funcionalidad que no existía en el momento en que se escribió esta respuesta. Recomiendo usar en jQuery.when()
lugar de estos enfoques, pero estoy dejando la respuesta con fines históricos.
-
Probablemente podría sobrevivir con un simple semáforo de conteo, aunque la forma en que lo implemente dependerá de su código. Un ejemplo simple sería algo así como ...
var semaphore = 0, // counting semaphore for ajax requests
all_queued = false; // bool indicator to account for instances where the first request might finish before the second even starts
semaphore++;
$.get('ajax/test1.html', function(data) {
semaphore--;
if (all_queued && semaphore === 0) {
// process your custom stuff here
}
});
semaphore++;
$.get('ajax/test2.html', function(data) {
semaphore--;
if (all_queued && semaphore === 0) {
// process your custom stuff here
}
});
semaphore++;
$.get('ajax/test3.html', function(data) {
semaphore--;
if (all_queued && semaphore === 0) {
// process your custom stuff here
}
});
semaphore++;
$.get('ajax/test4.html', function(data) {
semaphore--;
if (all_queued && semaphore === 0) {
// process your custom stuff here
}
});
// now that all ajax requests are queued up, switch the bool to indicate it
all_queued = true;
Si desea que esto funcione como {async: false} pero no desea bloquear el navegador, puede lograr lo mismo con una cola jQuery.
var $queue = $("<div/>");
$queue.queue(function(){
$.get('ajax/test1.html', function(data) {
$queue.dequeue();
});
}).queue(function(){
$.get('ajax/test2.html', function(data) {
$queue.dequeue();
});
}).queue(function(){
$.get('ajax/test3.html', function(data) {
$queue.dequeue();
});
}).queue(function(){
$.get('ajax/test4.html', function(data) {
$queue.dequeue();
});
});
.get()
. De esa manera al menos no duplicas ese código. ¡No solo eso, sino que declarando a function(){}
cada vez asigna memoria cada vez! Más bien una mala práctica si pudieras llamar a una función estáticamente definida.
JavaScript está basado en eventos, por lo que nunca debe esperar , en lugar de establecer ganchos / devoluciones de llamada
Probablemente solo pueda usar los métodos de éxito / completo de jquery.ajax
O podría usar .ajaxComplete :
$('.log').ajaxComplete(function(e, xhr, settings) {
if (settings.url == 'ajax/test.html') {
$(this).text('Triggered ajaxComplete handler.');
//and you can do whatever other processing here, including calling another function...
}
});
aunque debe publicar un pseudocódigo de cómo se llama (s) su (s) solicitud (es) ajax para ser más precisos ...
Una pequeña solución es algo como esto:
// Define how many Ajax calls must be done
var ajaxCalls = 3;
var counter = 0;
var ajaxCallComplete = function() {
counter++;
if( counter >= ajaxCalls ) {
// When all ajax calls has been done
// Do something like hide waiting images, or any else function call
$('*').css('cursor', 'auto');
}
};
var loadPersons = function() {
// Show waiting image, or something else
$('*').css('cursor', 'wait');
var url = global.ctx + '/loadPersons';
$.getJSON(url, function(data) {
// Fun things
})
.complete(function() { **ajaxCallComplete();** });
};
var loadCountries = function() {
// Do things
var url = global.ctx + '/loadCountries';
$.getJSON(url, function(data) {
// Travels
})
.complete(function() { **ajaxCallComplete();** });
};
var loadCities = function() {
// Do things
var url = global.ctx + '/loadCities';
$.getJSON(url, function(data) {
// Travels
})
.complete(function() { **ajaxCallComplete();** });
};
$(document).ready(function(){
loadPersons();
loadCountries();
loadCities();
});
La esperanza puede ser útil ...
jQuery le permite especificar si desea que la solicitud ajax sea asíncrona o no. Simplemente puede hacer que las solicitudes ajax sean síncronas y luego el resto del código no se ejecutará hasta que regresen.
Por ejemplo:
jQuery.ajax({
async: false,
//code
});
Si necesitas algo simple; devolución de llamada una vez y hecho
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
También podría usar async.js .
Creo que es mejor que $ .when porque puedes combinar todo tipo de llamadas asincrónicas que no admiten promesas listas para usar, como tiempos de espera, llamadas SqlLite, etc., y no solo solicitudes ajax.
Sobre la base de la respuesta @BBonifield, escribí una función de utilidad para que la lógica del semáforo no se extienda en todas las llamadas ajax.
untilAjax
es la función de utilidad que invoca una función de devolución de llamada cuando se completan todas las llamadas ajax.
ajaxObjs
es una matriz de objetos de configuración ajax [http://api.jquery.com/jQuery.ajax/]
.
fn
es la función de devolución de llamada
function untilAjax(ajaxObjs, fn) {
if (!ajaxObjs || !fn) {
return;
}
var ajaxCount = ajaxObjs.length,
succ = null;
for (var i = 0; i < ajaxObjs.length; i++) { //append logic to invoke callback function once all the ajax calls are completed, in success handler.
succ = ajaxObjs[i]['success'];
ajaxObjs[i]['success'] = function(data) { //modified success handler
if (succ) {
succ(data);
}
ajaxCount--;
if (ajaxCount == 0) {
fn(); //modify statement suitably if you want 'this' keyword to refer to another object
}
};
$.ajax(ajaxObjs[i]); //make ajax call
succ = null;
};
Ejemplo: doSomething
usos de funciones untilAjax
.
function doSomething() {
// variable declarations
untilAjax([{
url: 'url2',
dataType: 'json',
success: function(data) {
//do something with success data
}
}, {
url: 'url1',
dataType: 'json',
success: function(data) {
//do something with success data
}
}, {
url: 'url2',
dataType: 'json',
success: function(response) {
//do something with success data
}
}], function() {
// logic after all the calls are completed.
});
}
Le recomiendo usar $ .when () si está comenzando desde cero.
Aunque esta pregunta tiene más de un millón de respuestas, todavía no encontré nada útil para mi caso. Supongamos que tiene que lidiar con una base de código existente, que ya realiza algunas llamadas ajax y no quiere introducir la complejidad de las promesas y / o rehacer todo.
Podemos tomar ventaja de jQuery .data
, .on
y .trigger
las funciones que han sido una parte de jQuery desde siempre.
Lo bueno de mi solución es:
es obvio de qué depende exactamente la devolución de llamada
a la función triggerNowOrOnLoaded
no le importa si los datos ya se han cargado o si todavía estamos esperando por ellos
es muy fácil conectarlo a un código existente
$(function() {
// wait for posts to be loaded
triggerNowOrOnLoaded("posts", function() {
var $body = $("body");
var posts = $body.data("posts");
$body.append("<div>Posts: " + posts.length + "</div>");
});
// some ajax requests
$.getJSON("https://jsonplaceholder.typicode.com/posts", function(data) {
$("body").data("posts", data).trigger("posts");
});
// doesn't matter if the `triggerNowOrOnLoaded` is called after or before the actual requests
$.getJSON("https://jsonplaceholder.typicode.com/users", function(data) {
$("body").data("users", data).trigger("users");
});
// wait for both types
triggerNowOrOnLoaded(["posts", "users"], function() {
var $body = $("body");
var posts = $body.data("posts");
var users = $body.data("users");
$body.append("<div>Posts: " + posts.length + " and Users: " + users.length + "</div>");
});
// works even if everything has already loaded!
setTimeout(function() {
// triggers immediately since users have been already loaded
triggerNowOrOnLoaded("users", function() {
var $body = $("body");
var users = $body.data("users");
$body.append("<div>Delayed Users: " + users.length + "</div>");
});
}, 2000); // 2 seconds
});
// helper function
function triggerNowOrOnLoaded(types, callback) {
types = $.isArray(types) ? types : [types];
var $body = $("body");
var waitForTypes = [];
$.each(types, function(i, type) {
if (typeof $body.data(type) === 'undefined') {
waitForTypes.push(type);
}
});
var isDataReady = waitForTypes.length === 0;
if (isDataReady) {
callback();
return;
}
// wait for the last type and run this function again for the rest of the types
var waitFor = waitForTypes.pop();
$body.on(waitFor, function() {
// remove event handler - we only want the stuff triggered once
$body.off(waitFor);
triggerNowOrOnLoaded(waitForTypes, callback);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>Hi!</body>
Estoy usando la verificación de tamaño cuando se completa toda la carga ajax
function get_ajax(link, data, callback) {
$.ajax({
url: link,
type: "GET",
data: data,
dataType: "json",
success: function (data, status, jqXHR) {
callback(jqXHR.status, data)
},
error: function (jqXHR, status, err) {
callback(jqXHR.status, jqXHR);
},
complete: function (jqXHR, status) {
}
})
}
function run_list_ajax(callback){
var size=0;
var max= 10;
for (let index = 0; index < max; index++) {
var link = 'http://api.jquery.com/ajaxStop/';
var data={i:index}
get_ajax(link,data,function(status, data){
console.log(index)
if(size>max-2){
callback('done')
}
size++
})
}
}
run_list_ajax(function(info){
console.log(info)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
Para ampliar la respuesta de Alex, tengo un ejemplo con argumentos y promesas variables. Quería cargar imágenes a través de ajax y mostrarlas en la página después de que se hayan cargado todas.
Para hacer eso, utilicé lo siguiente:
let urlCreator = window.URL || window.webkitURL;
// Helper function for making ajax requests
let fetch = function(url) {
return $.ajax({
type: "get",
xhrFields: {
responseType: "blob"
},
url: url,
});
};
// Map the array of urls to an array of ajax requests
let urls = ["https://placekitten.com/200/250", "https://placekitten.com/300/250"];
let files = urls.map(url => fetch(url));
// Use the spread operator to wait for all requests
$.when(...files).then(function() {
// If we have multiple urls, then loop through
if(urls.length > 1) {
// Create image urls and tags for each result
Array.from(arguments).forEach(data => {
let imageUrl = urlCreator.createObjectURL(data[0]);
let img = `<img src=${imageUrl}>`;
$("#image_container").append(img);
});
}
else {
// Create image source and tag for result
let imageUrl = urlCreator.createObjectURL(arguments[0]);
let img = `<img src=${imageUrl}>`;
$("#image_container").append(img);
}
});
Actualizado para funcionar para urls individuales o múltiples: https://jsfiddle.net/euypj5w9/
Como se mencionó en otras respuestas, puede usar ajaxStop()
para esperar hasta que se completen todas las solicitudes de ajax.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
Si desea hacerlo para una ajax()
solicitud específica, lo mejor que puede hacer es utilizar el complete()
método dentro de la solicitud ajax determinada:
$.ajax({
type: "POST",
url: "someUrl",
success: function(data) {
// This function will be triggered when ajax returns a 200 status code (success)
},
complete: function() {
// This function will be triggered always, when ajax request is completed, even it fails/returns other status code
},
error: function() {
// This will be triggered when ajax request fail.
}
});
Pero, ¿ si necesita esperar solo unas pocas y ciertas solicitudes de ajax? Use las maravillosas promesas de JavaScript para esperar hasta que estos ajax que desea esperar estén listos. Hice un ejemplo breve, fácil y legible para mostrarle cómo funciona promesas con ajax.
Por favor, eche un vistazo al siguiente ejemplo . Solía setTimeout
aclarar el ejemplo.
// Note:
// resolve() is used to mark the promise as resolved
// reject() is used to mark the promise as rejected
$(document).ready(function() {
$("button").on("click", function() {
var ajax1 = new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "https://miro.medium.com/max/1200/0*UEtwA2ask7vQYW06.png",
xhrFields: { responseType: 'blob'},
success: function(data) {
setTimeout(function() {
$('#image1').attr("src", window.URL.createObjectURL(data));
resolve(" Promise ajax1 resolved");
}, 1000);
},
error: function() {
reject(" Promise ajax1 rejected");
},
});
});
var ajax2 = new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "https://cdn1.iconfinder.com/data/icons/social-media-vol-1-1/24/_github-512.png",
xhrFields: { responseType: 'blob' },
success: function(data) {
setTimeout(function() {
$('#image2').attr("src", window.URL.createObjectURL(data));
resolve(" Promise ajax2 resolved");
}, 1500);
},
error: function() {
reject(" Promise ajax2 rejected");
},
});
});
var ajax3 = new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: "https://miro.medium.com/max/632/1*LUfpOf7teWvPdIPTBmYciA.png",
xhrFields: { responseType: 'blob' },
success: function(data) {
setTimeout(function() {
$('#image3').attr("src", window.URL.createObjectURL(data));
resolve(" Promise ajax3 resolved");
}, 2000);
},
error: function() {
reject(" Promise ajax3 rejected");
},
});
});
Promise.all([ajax1, ajax2, ajax3]).then(values => {
console.log("We waited until ajax ended: " + values);
console.log("My few ajax ended, lets do some things!!")
}, reason => {
console.log("Promises failed: " + reason);
});
// Or if you want wait for them individually do it like this
// ajax1.then(values => {
// console.log("Promise 1 resolved: " + values)
// }, reason => {
// console.log("Promise 1 failed: " + reason)
// });
});
});
img {
max-width: 200px;
max-height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Make AJAX request</button>
<div id="newContent">
<img id="image1" src="">
<img id="image2" src="">
<img id="image3" src="">
</div>
Encontré manera simple, usando shift()
function waitReq(id)
{
jQuery.ajax(
{
type: 'POST',
url: ajaxurl,
data:
{
"page": id
},
success: function(resp)
{
...........
// check array length if not "0" continue to use next array value
if(ids.length)
{
waitReq(ids.shift()); // 2
)
},
error: function(resp)
{
....................
if(ids.length)
{
waitReq(ids.shift());
)
}
});
}
var ids = [1, 2, 3, 4, 5];
// shift() = delete first array value (then print)
waitReq(ids.shift()); // print 1
Mi solución es la siguiente
var request;
...
'services': {
'GetAddressBookData': function() {
//This is the primary service that loads all addressbook records
request = $.ajax({
type: "POST",
url: "Default.aspx/GetAddressBook",
contentType: "application/json;",
dataType: "json"
});
},
...
'apps': {
'AddressBook': {
'data': "",
'Start': function() {
...services.GetAddressBookData();
request.done(function(response) {
trace("ajax successful");
..apps.AddressBook.data = response['d'];
...apps.AddressBook.Filter();
});
request.fail(function(xhr, textStatus, errorThrown) {
trace("ajax failed - " + errorThrown);
});
Funcionó bastante bien. He intentado muchas formas diferentes de hacerlo, pero descubrí que es la más simple y reutilizable. Espero eso ayude
Mira mi solución:
1.Inserte esta función (y variable) en su archivo javascript:
var runFunctionQueue_callback;
function runFunctionQueue(f, index, callback) {
var next_index = index + 1
if (callback !== undefined) runFunctionQueue_callback = callback;
if (f[next_index] !== undefined) {
console.log(index + ' Next function avalaible -> ' + next_index);
$.ajax({
type: 'GET',
url: f[index].file,
data: (f[index].data),
complete: function() {
runFunctionQueue(f, next_index);
}
});
} else {
console.log(index + ' Last function');
$.ajax({
type: 'GET',
url: f[index].file,
data: (f[index].data),
async: false,
complete: runFunctionQueue_callback
});
}
}
2. Construya una matriz con sus solicitudes, como esta:
var f = [
{file: 'file_path', data: {action: 'action', data: 'any_data}},
{file: 'file_path', data: {action: 'action', data: 'any_data}},
{file: 'file_path', data: {action: 'action', data: 'any_data}},
{file: 'file_path', data: {action: 'action', data: 'any_data}}
];
3.Cree la función de devolución de llamada:
function Function_callback() {
alert('done');
}
4. Llame a la función runFunctionQueue con parámetros:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when
no funciona para mí, en callback(x)
lugar de return x
funcionar como se describe aquí: https://stackoverflow.com/a/13455253/10357604
Intenta de esta manera. haga un bucle dentro de la función de script java para esperar hasta que finalice la llamada ajax.
function getLabelById(id)
{
var label = '';
var done = false;
$.ajax({
cache: false,
url: "YourMvcActionUrl",
type: "GET",
dataType: "json",
async: false,
error: function (result) {
label='undefined';
done = true;
},
success: function (result) {
label = result.Message;
done = true;
}
});
//A loop to check done if ajax call is done.
while (!done)
{
setTimeout(function(){ },500); // take a sleep.
}
return label;
}
setTimeout()
NO take a sleep
. En este caso, simplemente bloquea todas las pestañas hasta que se done
haga realidad.
done
eso nunca será cierto mientras el ciclo while todavía se esté ejecutando. Si el ciclo while se está ejecutando, el ciclo de eventos no puede continuar y, por lo tanto, nunca ejecutará la devolución de llamada para el éxito de ajax.