Comment faire attendre une fonction jusqu'à ce que toutes les requêtes jQuery Ajax soient effectuées dans une autre fonction?
En bref, je dois attendre que toutes les requêtes Ajax soient effectuées avant d'exécuter la suivante. Mais comment?
Comment faire attendre une fonction jusqu'à ce que toutes les requêtes jQuery Ajax soient effectuées dans une autre fonction?
En bref, je dois attendre que toutes les requêtes Ajax soient effectuées avant d'exécuter la suivante. Mais comment?
Réponses:
jQuery définit désormais une fonction when à cet effet.
Il accepte un nombre illimité d'objets différés comme arguments et exécute une fonction lorsque tous se résolvent.
Cela signifie que si vous souhaitez lancer (par exemple) quatre requêtes ajax, puis effectuer une action une fois qu'elles sont terminées, vous pouvez faire quelque chose comme ceci:
$.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,
...
});
}
À mon avis, cela permet une syntaxe claire et claire, et évite d'impliquer des variables globales telles que ajaxStart et ajaxStop, qui pourraient avoir des effets secondaires indésirables lors du développement de votre page.
Si vous ne savez pas à l'avance combien d'arguments ajax vous devez attendre (c'est-à-dire que vous voulez utiliser un nombre variable d'arguments), cela peut toujours être fait mais c'est juste un peu plus compliqué. Voir Passer dans un tableau de différés à $ .when () (et peut-être jQuery .when lors du dépannage avec un nombre variable d'arguments ).
Si vous avez besoin d'un contrôle plus approfondi sur les modes de défaillance des scripts ajax, etc., vous pouvez enregistrer l'objet renvoyé par .when()
- c'est un objet jQuery Promise englobant toutes les requêtes ajax d'origine. Vous pouvez appeler .then()
ou .fail()
dessus pour ajouter des gestionnaires de réussite / échec détaillés.
$.when
renvoie un Promise
objet qui a des méthodes plus utiles, non seulement .done
. Par exemple, avec la .then(onSuccess, onFailure)
méthode, vous pouvez réagir lorsque les deux demandes aboutissent ou au moins l'une d'entre elles échoue.
fail
cas. Contrairement à done
, fail
tire immédiatement sur le premier échec et ne tient pas compte des différés restants.
onFailure
fonction pouvait être attachée. Comme je l'ai souligné dans un commentaire à la question du PO: il voudra peut-être indiquer plus précisément ce qu'il entendait par "fait". "Ryan Mohr" avait également un très bon point concernant le fait qu'il fail
se comporte différemment car done
, quelques lectures supplémentaires à faire sur Promises
je suppose que html5rocks.com/en/tutorials/es6/promises
Si vous voulez savoir quand toutes les ajax
demandes sont terminées dans votre document, quel que soit leur nombre, utilisez simplement l' événement $ .ajaxStop de cette façon:
$(document).ajaxStop(function () {
// 0 === $.active
});
Dans ce cas, vous n'avez pas besoin de deviner combien de demandes se produisent dans l'application, qui pourraient se terminer à l'avenir, ni de creuser dans les fonctions de logique complexe ou de trouver les fonctions qui font des
HTTP(S)
demandes.
$.ajaxStop
ici peut également être lié à n'importe quelHTML
nœud qui, selon vous, pourrait être modifié par requst.
Mise à jour:
si vous souhaitez vous en tenir à la ES
syntaxe, vous pouvez utiliser Promise.all pour les ajax
méthodes connues :
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Un point intéressant ici est qu'il fonctionne à la fois avec Promises
et avec les $.ajax
requêtes.
Voici la démonstration de jsFiddle .
Mise à jour 2:
version encore plus récente utilisant la syntaxe async / wait :
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
J'ai trouvé une bonne réponse par gnarf mon auto qui est exactement ce que je cherchais :)
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);
Ensuite, vous pouvez ajouter une demande ajax à la file d'attente comme ceci:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Utilisez l' ajaxStop
événement.
Par exemple, supposons que vous ayez un message de chargement ... lors de la récupération de 100 demandes ajax et que vous souhaitiez masquer ce message une fois chargé.
Depuis le doc jQuery :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Notez qu'il attendra que toutes les requêtes ajax soient effectuées sur cette page.
REMARQUE: les réponses ci-dessus utilisent des fonctionnalités qui n'existaient pas au moment où cette réponse a été écrite. Je recommande d'utiliser à la jQuery.when()
place de ces approches, mais je laisse la réponse à des fins historiques.
-
Vous pourriez probablement vous en tirer avec un simple sémaphore de comptage, bien que la façon dont vous l'implémentiez dépendrait de votre code. Un exemple simple serait quelque chose comme ...
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 vous vouliez que cela fonctionne comme {async: false} mais que vous ne vouliez pas verrouiller le navigateur, vous pourriez accomplir la même chose avec une file d'attente 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 cette façon, au moins, vous ne dupliquez pas ce code. Non seulement cela, mais déclarer un à function(){}
chaque fois alloue de la mémoire à chaque fois! Plutôt mauvaise pratique si vous pouviez appeler une fonction définie statiquement.
javascript est basé sur les événements, vous ne devez donc jamais attendre , plutôt définir des hooks / rappels
Vous pouvez probablement simplement utiliser les méthodes succès / complet de jquery.ajax
Ou vous pouvez utiliser .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...
}
});
bien que vous devriez publier un pseudocode de la façon dont votre (vos) demande (s) ajax est (sont) appelée (s) pour être plus précis ...
Une petite solution de contournement est quelque chose comme ceci:
// 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();
});
L'espoir peut être utile ...
jQuery vous permet de spécifier si vous souhaitez que la demande ajax soit asynchrone ou non. Vous pouvez simplement rendre les requêtes ajax synchrones, puis le reste du code ne s'exécutera pas avant leur retour.
Par exemple:
jQuery.ajax({
async: false,
//code
});
Si vous avez besoin de quelque chose de simple; rappel terminé
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Vous pouvez également utiliser async.js .
Je pense que c'est mieux que $ .when parce que vous pouvez fusionner toutes sortes d'appels asynchrones qui ne prennent pas en charge les promesses prêtes à l'emploi comme les délais d'attente, les appels SqlLite, etc. et pas seulement les demandes ajax.
Sur la base de la réponse @BBonifield, j'ai écrit une fonction utilitaire pour que la logique sémaphore ne se répande pas dans tous les appels ajax.
untilAjax
est la fonction utilitaire qui appelle une fonction de rappel lorsque tous les ajaxCalls sont terminés.
ajaxObjs
est un tableau d'objets de réglage ajax [http://api.jquery.com/jQuery.ajax/]
.
fn
est la fonction de rappel
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;
};
Exemple: doSomething
fonction utilise 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.
});
}
Je recommande fortement d'utiliser $ .when () si vous partez de zéro.
Même si cette question a plus de millions de réponses, je n'ai toujours rien trouvé d'utile pour mon cas. Disons que vous devez gérer une base de code existante, faire déjà des appels ajax et ne pas vouloir introduire la complexité des promesses et / ou refaire le tout.
Nous pouvons facilement profiter de jQuery .data
, .on
et.trigger
les fonctions qui ont été une partie de jQuery depuis toujours.
Le bon côté de ma solution est:
il est évident de quoi dépend exactement le rappel
la fonction triggerNowOrOnLoaded
ne se soucie pas si les données ont déjà été chargées ou si nous l'attendons toujours
il est super facile de le brancher sur un code existant
$(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>
J'utilise la vérification de la taille lorsque toute la charge ajax est terminée
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>
Pour développer la réponse d'Alex, j'ai un exemple avec des arguments et des promesses variables. Je voulais charger des images via ajax et les afficher sur la page après qu'elles soient toutes chargées.
Pour ce faire, j'ai utilisé ce qui suit:
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);
}
});
Mis à jour pour fonctionner pour des URL simples ou multiples: https://jsfiddle.net/euypj5w9/
Comme d'autres réponses mentionnées, vous pouvez utiliser ajaxStop()
pour attendre que toutes les demandes ajax soient terminées.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
Si vous voulez le faire pour une ajax()
demande spécifique, le mieux que vous puissiez faire est d'utiliser la complete()
méthode à l'intérieur de la certaine demande 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.
}
});
Mais, si vous devez attendre seulement quelques demandes ajax et certaines à faire? Utilisez les merveilleuses promesses de javascript pour attendre que ces ajax que vous voulez attendre soient terminés. J'ai fait un exemple bref, facile et lisible pour vous montrer comment fonctionne les promesses avec ajax.
Veuillez jeter un œil à l'exemple suivant . J'avais l'habitude setTimeout
de clarifier l'exemple.
// 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>
J'ai trouvé un moyen simple, en utilisant 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
Ma solution est la suivante
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);
});
A très bien fonctionné. J'ai essayé de nombreuses façons différentes de le faire, mais j'ai trouvé que c'était le plus simple et le plus réutilisable. J'espère que cela aide
Regardez ma solution:
1.Insérez cette fonction (et variable) dans votre fichier 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
});
}
}
Construisez un tableau avec vos demandes, comme ceci:
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.Créer une fonction de rappel:
function Function_callback() {
alert('done');
}
4.Appelez la fonction runFunctionQueue avec les paramètres:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when
ne fonctionne pas pour moi, callback(x)
au lieu de return x
travailler comme décrit ici: https://stackoverflow.com/a/13455253/10357604
Essayez de cette façon. faire une boucle à l'intérieur de la fonction de script java pour attendre la fin de l'appel 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()
n'est PAS le cas take a sleep
. Dans ce cas, il vous suffit de bloquer tous les onglets jusqu'à ce qu'il done
devienne vrai.
done
ce ne sera jamais vrai tant que la boucle while est toujours en cours d'exécution. Si la boucle while est en cours d'exécution, la boucle d'événement ne peut pas continuer et ne lancera donc jamais le rappel vers le succès ajax.