Come faccio ad aspettare una funzione fino a quando tutte le richieste jQuery Ajax non vengono eseguite all'interno di un'altra funzione?
In breve, devo aspettare che vengano fatte tutte le richieste Ajax prima di eseguire il successivo. Ma come?
Come faccio ad aspettare una funzione fino a quando tutte le richieste jQuery Ajax non vengono eseguite all'interno di un'altra funzione?
In breve, devo aspettare che vengano fatte tutte le richieste Ajax prima di eseguire il successivo. Ma come?
Risposte:
jQuery ora definisce una funzione quando per questo scopo.
Accetta qualsiasi numero di oggetti rinviati come argomenti ed esegue una funzione quando tutti risolvono.
Ciò significa che se si desidera avviare (ad esempio) quattro richieste Ajax, quindi eseguire un'azione al termine, è possibile fare qualcosa del genere:
$.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,
...
});
}
A mio avviso, crea una sintassi chiara e chiara ed evita il coinvolgimento di variabili globali come ajaxStart e ajaxStop, che potrebbero avere effetti collaterali indesiderati durante lo sviluppo della pagina.
Se non sai in anticipo quanti argomenti ajax devi aspettare (cioè vuoi usare un numero variabile di argomenti), può ancora essere fatto ma è solo un po 'più complicato. Vedi Passa in una matrice di Deferreds a $ .when () (e forse jQuery .quando si risolve con un numero variabile di argomenti ).
Se hai bisogno di un controllo più approfondito sulle modalità di errore degli script ajax ecc., Puoi salvare l'oggetto restituito da .when()
: è un oggetto jQuery Promise che comprende tutte le query ajax originali. È possibile chiamare .then()
o .fail()
su di esso per aggiungere gestori di successo / fallimento dettagliati.
$.when
restituisce un Promise
oggetto che ha metodi più utili, non solo .done
. Ad esempio, con il .then(onSuccess, onFailure)
metodo è possibile reagire quando entrambe le richieste hanno esito positivo o almeno una di esse non riesce.
fail
caso. Diversamente done
, fail
il primo colpo fallisce immediatamente e ignora i rimanenti rinvii.
onFailure
funzione potrebbe essere collegata. Come ho sottolineato in un commento alla domanda del PO: potrebbe voler indicare più precisamente cosa intendesse per "fatto". "Ryan Mohr" ha anche avuto un ottimo punto in merito al fatto che fail
si comporta in modo diverso poiché done
, alcune ulteriori letture da fare su Promises
suppongo html5rocks.com/en/tutorials/es6/promises
Se vuoi sapere quando tutte le ajax
richieste sono finite nel tuo documento, indipendentemente da quante ne esistano, usa semplicemente l' evento $ .ajaxStop in questo modo:
$(document).ajaxStop(function () {
// 0 === $.active
});
In questo caso, non è necessario indovinare quante richieste stanno accadendo nell'applicazione, che potrebbero finire in futuro, né scavare in funzioni logiche complesse o trovare quali funzioni stanno facendo
HTTP(S)
richieste.
$.ajaxStop
qui può anche essere associato a qualsiasiHTML
nodo che ritieni possa essere modificato da requst.
Aggiornamento:
se si desidera attenersi alla ES
sintassi, è possibile utilizzare Promise.all per ajax
metodi noti :
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Un punto interessante è che funziona sia con Promises
e $.ajax
richieste.
Ecco la dimostrazione di jsFiddle .
Aggiornamento 2:
versione ancora più recente che utilizza la sintassi async / await :
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
Ho trovato una buona risposta da parte gnarf la mia auto, che è esattamente quello che stavo cercando :)
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);
Quindi puoi aggiungere una richiesta Ajax alla coda in questo modo:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Usa l' ajaxStop
evento.
Ad esempio, supponiamo che tu abbia un messaggio di caricamento ... durante il recupero di 100 richieste Ajax e desideri nascondere quel messaggio una volta caricato.
Dal documento jQuery :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Nota che attenderà che tutte le richieste Ajax vengano fatte su quella pagina.
NOTA: le risposte di cui sopra utilizzano funzionalità che non esistevano al momento in cui questa risposta è stata scritta. Raccomando di usarejQuery.when()
invece di questi approcci, ma lascio la risposta per scopi storici.
-
Probabilmente potresti cavartela con un semplice semaforo di conteggio, sebbene il modo in cui lo implementerai dipenderà dal tuo codice. Un semplice esempio potrebbe essere qualcosa come ...
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 volevi che funzionasse come {async: false} ma non volevi bloccare il browser, potresti ottenere lo stesso risultato con una coda 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()
. In questo modo almeno non duplicare quel codice. Non solo, ma dichiarando function(){}
ogni volta alloca memoria ogni volta! Piuttosto cattiva pratica se si potesse chiamare una funzione definita staticamente.
javascript è basato su eventi, quindi non dovresti mai aspettare , piuttosto imposta hook / callback
Probabilmente puoi semplicemente usare i metodi success / complete di jquery.ajax
Oppure potresti usare .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...
}
});
anche se dovresti pubblicare uno pseudocodice di come le tue richieste ajax vengono chiamate per essere più precise ...
Una piccola soluzione è qualcosa del genere:
// 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 speranza può essere utile ...
jQuery consente di specificare se si desidera che la richiesta ajax sia asincrona o meno. Puoi semplicemente rendere sincrone le richieste ajax e il resto del codice non verrà eseguito fino alla loro restituzione.
Per esempio:
jQuery.ajax({
async: false,
//code
});
Se hai bisogno di qualcosa di semplice; una volta e richiamata
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Inoltre puoi usare async.js .
Penso che sia meglio di $ .quando perché è possibile unire tutti i tipi di chiamate asincrone che non supportano le promesse out-of-the-box come timeout, chiamate SqlLite ecc. E non solo richieste Ajax.
Sulla base della risposta di @BBonifield, ho scritto una funzione di utilità in modo che la logica dei semafori non si diffonda in tutte le chiamate ajax.
untilAjax
è la funzione di utilità che richiama una funzione di richiamata quando tutte le chiamate ajax sono state completate.
ajaxObjs
è una matrice di oggetti di impostazione Ajax [http://api.jquery.com/jQuery.ajax/]
.
fn
è la funzione di richiamata
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;
};
Esempio: la doSomething
funzione 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.
});
}
Consiglio vivamente di usare $ .when () se inizi da zero.
Anche se questa domanda ha oltre un milione di risposte, non ho ancora trovato nulla di utile per il mio caso. Supponiamo che tu abbia a che fare con una base di codice esistente, già facendo alcune chiamate Ajax e non vuoi introdurre la complessità delle promesse e / o rifare il tutto.
Possiamo facilmente usufruire di jQuery .data
, .on
e .trigger
funzioni che hanno fatto parte di jQuery da sempre.
La cosa buona della mia soluzione è:
è ovvio da cosa dipende esattamente il callback
alla funzione triggerNowOrOnLoaded
non importa se i dati sono già stati caricati o se li stiamo ancora aspettando
è semplicissimo collegarlo a un codice esistente
$(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>
Sto usando il controllo dimensioni quando tutto il caricamento Ajax è stato completato
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>
Per espandere la risposta di Alex, ho un esempio con argomenti e promesse variabili. Volevo caricare le immagini tramite Ajax e visualizzarle sulla pagina dopo averle tutte caricate.
Per fare ciò, ho usato quanto segue:
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);
}
});
Aggiornato per funzionare con URL singoli o multipli: https://jsfiddle.net/euypj5w9/
Come altre risposte menzionate, puoi usare ajaxStop()
per attendere fino al completamento di tutte le richieste Ajax.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
Se vuoi farlo per una ajax()
richiesta specifica, la cosa migliore che puoi fare è usare il complete()
metodo all'interno di una determinata richiesta 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.
}
});
Ma, se hai bisogno di aspettare solo poche e certe richieste ajax da fare? Usa le meravigliose promesse javascript per aspettare fino a quando questi ajax che vuoi aspettare sono terminati. Ho fatto un esempio breve, facile e leggibile per mostrarti come funzionano le promesse con Ajax.
Dai un'occhiata al prossimo esempio . Ho usato setTimeout
per chiarire l'esempio.
// 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>
Ho trovato un modo semplice, 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
La mia soluzione è la seguente
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);
});
Ha funzionato abbastanza bene. Ho provato molti modi diversi per farlo, ma ho scoperto che questo è il più semplice e riutilizzabile. Spero che sia d'aiuto
Guarda la mia soluzione:
1.Inserire questa funzione (e variabile) nel file 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. Buil un array con le tue richieste, in questo modo:
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.Creazione della funzione di richiamata:
function Function_callback() {
alert('done');
}
4.Chiama la funzione runFunctionQueue con parametri:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when
non funziona per me, callback(x)
invece di return x
funzionare come descritto qui: https://stackoverflow.com/a/13455253/10357604
Prova in questo modo. crea un ciclo all'interno della funzione java script per attendere fino al termine della chiamata 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()
NON take a sleep
. In questo caso, basta bloccare tutte le schede fino a quando non done
diventa vero.
done
non sarà mai vero mentre il ciclo while è ancora in esecuzione. Se il ciclo while è in esecuzione, il ciclo degli eventi non può continuare e pertanto non eseguirà mai la richiamata per il successo ajax.