Como faço para uma função esperar até que todas as solicitações jQuery Ajax sejam feitas dentro de outra função?
Em resumo, preciso aguardar que todas as solicitações do Ajax sejam feitas antes de executar a próxima. Mas como?
Como faço para uma função esperar até que todas as solicitações jQuery Ajax sejam feitas dentro de outra função?
Em resumo, preciso aguardar que todas as solicitações do Ajax sejam feitas antes de executar a próxima. Mas como?
Respostas:
O jQuery agora define uma função when para esse propósito.
Ele aceita qualquer número de objetos adiados como argumentos e executa uma função quando todos eles forem resolvidos.
Isso significa que, se você deseja iniciar (por exemplo) quatro solicitações ajax, execute uma ação quando elas forem concluídas, você pode fazer algo assim:
$.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,
...
});
}
Na minha opinião, cria uma sintaxe limpa e clara e evita o envolvimento de variáveis globais como ajaxStart e ajaxStop, que podem ter efeitos colaterais indesejados à medida que a página se desenvolve.
Se você não sabe de antemão quantos argumentos ajax precisa esperar (ou seja, deseja usar um número variável de argumentos), isso ainda pode ser feito, mas é um pouco mais complicado. Consulte Passar em uma matriz de adiados para $ .when () (e talvez jQuery .when ao solucionar problemas com número variável de argumentos ).
Se você precisar de um controle mais profundo sobre os modos de falha dos scripts ajax, etc., poderá salvar o objeto retornado .when()
- é um objeto jQuery Promise que abrange todas as consultas ajax originais. Você pode ligar .then()
ou .fail()
nele para adicionar manipuladores detalhados sucesso / fracasso.
$.when
retorna um Promise
objeto que possui métodos mais úteis, não apenas .done
. Por exemplo, com o .then(onSuccess, onFailure)
método, você pode reagir quando as duas solicitações forem bem-sucedidas ou pelo menos uma delas falhar.
fail
caso. Ao contrário done
, os fail
disparos imediatamente na primeira falha e desconsideram os demais adiados.
onFailure
função pode ser anexada. Como indiquei em um comentário à pergunta do OP: ele pode querer indicar com mais precisão o que ele quis dizer com "pronto". "Ryan Mohr" também tinha um ponto muito bom sobre o fato de que fail
se comporta de forma diferente como done
, alguns mais leitura a ser feito sobre Promises
Acho html5rocks.com/en/tutorials/es6/promises
Se você quiser saber quando todas as ajax
solicitações foram concluídas no seu documento, não importa quantas delas existam, basta usar o evento $ .ajaxStop desta maneira:
$(document).ajaxStop(function () {
// 0 === $.active
});
Nesse caso, você não precisa adivinhar quantas solicitações estão acontecendo no aplicativo, que podem terminar no futuro, nem se aprofundar nas funções de lógica complexa ou descobrir quais funções estão fazendo
HTTP(S)
solicitações.
$.ajaxStop
aqui também pode ser associado a qualquerHTML
nó que você acha que pode ser modificado pelo requst.
Atualização:
se você deseja manter a ES
sintaxe, pode usar Promise.all para ajax
métodos conhecidos :
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Um ponto interessante aqui é que ele trabalha com Promises
e $.ajax
solicita.
Aqui está a demonstração do jsFiddle .
Atualização 2:
versão ainda mais recente usando a sintaxe assíncrona / aguardada :
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
Eu encontrei uma boa resposta por gnarf my self, que é exatamente o que eu estava procurando :)
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);
Em seguida, você pode adicionar uma solicitação ajax à fila como esta:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Use o ajaxStop
evento.
Por exemplo, digamos que você tenha uma mensagem carregando ... enquanto busca 100 solicitações ajax e deseja ocultar essa mensagem depois de carregada.
No documento jQuery :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Observe que ele aguardará todas as solicitações de ajax serem feitas nessa página.
NOTA: As respostas acima usam funcionalidades que não existiam no momento em que essa resposta foi escrita. Eu recomendo usar jQuery.when()
essas abordagens, mas estou deixando a resposta para fins históricos.
-
Você provavelmente poderia se dar bem com um semáforo de contagem simples, embora a maneira como você o implemente dependa do seu código. Um exemplo simples seria algo 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;
Se você quiser que isso funcione como {async: false}, mas não queira bloquear o navegador, poderá realizar o mesmo com uma fila 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()
. Dessa forma, pelo menos você não duplica esse código. Não apenas isso, mas declarar function(){}
cada vez aloca memória cada vez! Em vez disso, é uma prática ruim se você puder chamar uma função definida estaticamente.
O javascript é baseado em eventos; portanto, você nunca deve esperar ; em vez disso, defina ganchos / retornos de chamada
Provavelmente, você pode apenas usar os métodos de sucesso / completo do jquery.ajax
Ou você pode 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...
}
});
embora você deva postar um pseudocódigo de como suas solicitações ajax são chamadas para serem mais precisas ...
Uma pequena solução alternativa é algo como isto:
// 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();
});
A esperança pode ser útil ...
O jQuery permite especificar se você deseja que a solicitação ajax seja assíncrona ou não. Você pode simplesmente tornar as solicitações ajax síncronas e o restante do código não será executado até que retornem.
Por exemplo:
jQuery.ajax({
async: false,
//code
});
Se você precisar de algo simples; retorno de chamada único e concluído
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Além disso, você pode usar o async.js .
Eu acho que é melhor do que $. Quando, porque você pode mesclar todos os tipos de chamadas assíncronas que não suportam promessas prontas, como tempos limite, chamadas do SqlLite etc. e não apenas solicitações de ajax.
Com base na resposta do @BBonifield, escrevi uma função de utilitário para que a lógica do semáforo não seja espalhada em todas as chamadas ajax.
untilAjax
é a função de utilitário que chama uma função de retorno de chamada quando todos os ajaxCalls são concluídos.
ajaxObjs
é uma matriz de objetos de configuração do ajax [http://api.jquery.com/jQuery.ajax/]
.
fn
é função de retorno de chamada
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;
};
Exemplo: doSomething
função usa 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.
});
}
Eu recomendo usar $ .when () se você estiver começando do zero.
Embora essa pergunta tenha mais de um milhão de respostas, ainda não encontrei nada útil para o meu caso. Digamos que você precise lidar com uma base de código existente, já fazendo algumas chamadas ajax e não queira introduzir a complexidade das promessas e / ou refazer tudo.
Podemos facilmente tirar proveito do jQuery .data
, .on
e .trigger
funções que têm sido uma parte do jQuery desde sempre.
O lado bom da minha solução é:
é óbvio do que o retorno de chamada depende exatamente
a função triggerNowOrOnLoaded
não se importa se os dados já foram carregados ou ainda estamos esperando por eles
é super fácil conectá-lo a um 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>
Estou usando a verificação de tamanho quando toda a carga do ajax foi concluída
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 expandir a resposta de Alex, tenho um exemplo com argumentos e promessas variáveis. Eu queria carregar imagens via ajax e exibi-las na página depois que todas foram carregadas.
Para fazer isso, usei o seguinte:
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);
}
});
Atualizado para funcionar com URLs únicos ou múltiplos: https://jsfiddle.net/euypj5w9/
Como outras respostas mencionadas, você pode ajaxStop()
aguardar até que todas as solicitações de ajax sejam concluídas.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
Se você deseja fazer isso para uma ajax()
solicitação específica, o melhor que pode fazer é usar o complete()
método dentro de uma determinada solicitação ajax:
$.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.
}
});
Mas, se você precisar esperar apenas algumas e certas solicitações de ajax serem feitas? Use o maravilhoso javascript promete esperar até que o ajax que você deseja esperar esteja pronto. Fiz um exemplo breve, fácil e legível para mostrar como as promessas funcionam com o ajax.
Por favor, dê uma olhada no próximo exemplo . Eu costumava setTimeout
esclarecer o exemplo.
// 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>
Eu achei uma maneira simples, 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
Minha solução é a seguinte
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);
});
Funcionou muito bem. Eu tentei várias maneiras diferentes de fazer isso, mas achei a mais simples e a mais reutilizável. Espero que ajude
Veja a minha solução:
1.Insira esta função (e variável) no seu arquivo 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.Configure uma matriz com seus pedidos, desta forma:
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}}
];
Função 3.Create callback:
function Function_callback() {
alert('done');
}
4. Ligue para a função runFunctionQueue com os 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
não funciona para mim, em callback(x)
vez de return x
funcionar como descrito aqui: https://stackoverflow.com/a/13455253/10357604
Tente assim. faça um loop dentro da função de script java para esperar até que a chamada ajax termine.
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()
NÃO take a sleep
. Nesse caso, você apenas bloqueia todas as guias até que done
se torne realidade.
done
nunca seja verdade enquanto o loop while ainda estiver em execução. Se o loop while estiver em execução, o loop de eventos não poderá continuar e, portanto, nunca executará o retorno de chamada para o sucesso do ajax.