Comment faire en sorte qu'une fonction attende que toutes les demandes Ajax de jQuery soient effectuées dans une autre fonction?
En bref, je dois attendre que toutes les demandes Ajax soient effectuées avant d'exécuter la prochaine. Mais comment?
jQuery définit maintenant un when function à cette fin.
Il accepte un nombre quelconque d'objets différés en tant qu'arguments et exécute une fonction lorsque tous les résolvent.
Cela signifie que si vous voulez lancer (par exemple) quatre demandes ajax, puis exécuter une action quand 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 donne une syntaxe propre et claire, et évite d’impliquer des variables globales telles que ajaxStart et ajaxStop, qui pourraient avoir des effets secondaires indésirables au fur et à mesure 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 un peu plus compliqué. Voir Passer un tableau de Deferreds à $ .when () (et peut-être jQuery .when 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()
. Il s'agit d'un objet jQuery Promise qui englobe toutes les requêtes ajax d'origine. Vous pouvez appeler .then()
ou .fail()
pour ajouter des gestionnaires de réussite/échec détaillés.
Si vous voulez attendre que toutes les requêtes ajax soient terminées dans votre document, quel que soit le nombre d'entre elles, utilisez simplement $.ajaxStop
événement de cette façon:
_ $(document).ajaxStop(function () {
// 0 === $.active
});
_
Dans ce cas, il n’est pas nécessaire de deviner le nombre de demandes pouvant se terminer dans une application. Dans certains cas, les requêtes ajax peuvent faire partie de la logique interne d'une fonction, ce qui peut être assez compliqué (par exemple, appeler d'autres fonctions). Dans ce cas, vous ne pouvez pas attendre que cette fonction soit terminée avec toute sa logique plutôt que seulement attend la fin de la partie ajax
.
$.ajaxStop
ici peut également être lié à tout nœud HTML
que vous pensez susceptible d'être modifié par ajax
.
Encore une fois, le but de ce gestionnaire est de savoir quand il n'y a pas actif ajax
de ne pas effacer ou réinitialiser quelque chose.
P.S. Si la syntaxe ES6 ne vous dérange pas, vous pouvez utiliser _Promise.all
_ pour les méthodes connues ajax
. Exemple:
_Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
_
Un point intéressant ici est que cela fonctionne à la fois avec les requêtes Promises
et _$.ajax
_. Voici jsFiddle démontrant le dernier.
J'ai trouvé un bonne réponse par gnarf mon moi 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 requête ajax à la file d'attente comme ceci:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
NOTE: Les réponses ci-dessus utilisent une fonctionnalité qui n'existait pas au moment où cette réponse a été écrite. Je recommande d'utiliser jQuery.when()
au lieu 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 sa mise en œuvre dépende 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 souhaitez que cela fonctionne comme {async: false} mais que vous ne voulez pas verrouiller le navigateur, vous pouvez effectuer 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();
});
});
Utilisez l'événement ajaxStop
.
Par exemple, supposons que vous ayez un message en cours de chargement ... lors de l'extraction de 100 demandes ajax et que vous souhaitiez masquer ce message une fois chargé.
De la jQuery doc :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Notez qu'il attendra que toutes les requêtes ajax soient effectuées sur cette page.
Une solution de contournement ressemble à 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 ...
javascript est basé sur les événements, vous ne devriez donc jamais attendre , définissez plutôt les hooks/callbacks
Vous pouvez probablement simplement utiliser les méthodes de réussite/complète 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 deviez poster un pseudocode indiquant comment votre (vos) demande (s) ajax est (sont) appelée (s) pour être plus précise ...
Comme indiqué dans les autres réponses, 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 demande spécifique ajax()
, le mieux que vous puissiez faire est d'utiliser la méthode complete()
à l'intérieur de certaines requêtes 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 n’avez besoin d’attendre que quelques requêtes ajax? Utilisez le merveilleux javascript promesses d’attendre jusqu'à ce que ces ajax que vous voulez attendre soient terminés. J'ai brièvement créé un exemple facile et lisible pour vous montrer comment fonctionne promesse avec ajax.
Veuillez vous reporter à l'exemple suivant . J'ai utilisé setTimeout
pour 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>
jQuery vous permet de spécifier si vous souhaitez que la demande ajax soit asynchrone ou non. Vous pouvez simplement rendre les demandes ajax synchrones et le reste du code ne sera pas exécuté avant leur retour.
Par exemple:
jQuery.ajax({
async: false,
//code
});
Si vous avez besoin de quelque chose de simple rappel une fois fait
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
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 leur chargement.
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);
}
});
Mise à jour pour fonctionner avec une ou plusieurs URL: https://jsfiddle.net/euypj5w9/
J'utilise la vérification de la taille lorsque tous les travaux ajax sont terminés
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>
$.when
ne fonctionne pas pour moi, callback(x)
au lieu de return x
a fonctionné comme décrit ici: https://stackoverflow.com/a/13455253/10357604
Sur la base de la réponse @BBonifield, j'ai écrit une fonction utilitaire afin que la logique de sémaphore ne soit pas étendue à 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 une 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: la fonction doSomething
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.
});
}
Aussi, vous pouvez utiliser async.js .
Je pense que c'est mieux que $ .when, car 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.
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. Supposons que vous ayez à traiter avec une base de code existante, que vous passiez déjà quelques appels ajax et que vous ne vouliez pas introduire la complexité des promesses et/ou refaire le tout.
Nous pouvons facilement tirer parti des fonctions jQuery .data
, .on
et .trigger
qui font partie de jQuery depuis toujours.
Les bonnes choses à propos de ma solution sont:
il est évident que le rappel dépend exactement
la fonction triggerNowOrOnLoaded
importe peu si les données ont déjà été chargées ou si nous les attendons toujours
c'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>
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 fonctionné assez bien. J'ai essayé beaucoup de façons différentes de le faire, mais j'ai trouvé que c'était la plus simple et la plus réutilisable. J'espère que ça aide
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
Regardez ma solution:
1.Insérez cette fonction (et cette 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
});
}
}
2.Buil 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}}
];
Fonction de rappel 3.Create:
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
J'ai rencontré ce problème et créé un plugin générique jquery_counter pour le résoudre: https://bitbucket.org/stxnext/jquery_counter/