jQuery callback pour plusieurs appels ajax
je veux faire trois appels ajax dans un événement de clic. Chaque appel ajax effectue une opération distincte et renvoie les données nécessaires pour un rappel final. Les appels eux-mêmes ne dépendent pas l'un de l'autre, ils peuvent tous aller en même temps, cependant je voudrais avoir un rappel final quand tous les trois sont terminés.
$('#button').click(function() {
fun1();
fun2();
fun3();
//now do something else when the requests have done their 'success' callbacks.
});
var fun1= (function() {
$.ajax({/*code*/});
});
var fun2 = (function() {
$.ajax({/*code*/});
});
var fun3 = (function() {
$.ajax({/*code*/});
});
12 réponses
voici un objet de callback que j'ai écrit où vous pouvez soit définir un seul callback pour tirer une fois que tout est terminé ou laisser chacun avoir son propre callback et les virer tous une fois que tout est terminé:
AVIS
depuis jQuery 1.5+ vous pouvez utiliser la méthode différée comme décrit dans une autre réponse:
$.when($.ajax(), [...]).then(function(results){},[...]);
pour jQuery < 1,5 ce qui suit fonctionnera ou si vous avez besoin de faire démarrer vos appels ajax à des heures inconnues comme montré ici avec deux boutons: tiré après que les deux boutons sont cliqués
[utilisation]
pour unique rappel une fois terminé: Exemple
// initialize here
var requestCallback = new MyRequestsCompleted({
numRequest: 3,
singleCallback: function(){
alert( "I'm the callback");
}
});
//usage in request
$.ajax({
url: '/echo/html/',
success: function(data) {
requestCallback.requestComplete(true);
}
});
$.ajax({
url: '/echo/html/',
success: function(data) {
requestCallback.requestComplete(true);
}
});
$.ajax({
url: '/echo/html/',
success: function(data) {
requestCallback.requestComplete(true);
}
});
chacune ayant leur propre rappel lorsque tout est terminé: exemple pratique
//initialize
var requestCallback = new MyRequestsCompleted({
numRequest: 3
});
//usage in request
$.ajax({
url: '/echo/html/',
success: function(data) {
requestCallback.addCallbackToQueue(true, function() {
alert('Im the first callback');
});
}
});
$.ajax({
url: '/echo/html/',
success: function(data) {
requestCallback.addCallbackToQueue(true, function() {
alert('Im the second callback');
});
}
});
$.ajax({
url: '/echo/html/',
success: function(data) {
requestCallback.addCallbackToQueue(true, function() {
alert('Im the third callback');
});
}
});
[Le Code]
var MyRequestsCompleted = (function() {
var numRequestToComplete, requestsCompleted, callBacks, singleCallBack;
return function(options) {
if (!options) options = {};
numRequestToComplete = options.numRequest || 0;
requestsCompleted = options.requestsCompleted || 0;
callBacks = [];
var fireCallbacks = function() {
alert("we're all complete");
for (var i = 0; i < callBacks.length; i++) callBacks[i]();
};
if (options.singleCallback) callBacks.push(options.singleCallback);
this.addCallbackToQueue = function(isComplete, callback) {
if (isComplete) requestsCompleted++;
if (callback) callBacks.push(callback);
if (requestsCompleted == numRequestToComplete) fireCallbacks();
};
this.requestComplete = function(isComplete) {
if (isComplete) requestsCompleted++;
if (requestsCompleted == numRequestToComplete) fireCallbacks();
};
this.setCallback = function(callback) {
callBacks.push(callBack);
};
};
})();
on dirait que vous avez quelques réponses à cela, mais je pense qu'il y a quelque chose qui mérite d'être mentionné ici qui simplifiera grandement votre code. jQuery a introduit le $.when
dans v1.5. On dirait:
$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
//this callback will be fired once all ajax calls have finished.
});
ne l'a pas vu mentionné ici, j'espère que ça aidera.
ne voyant pas le besoin d'un objet malarky moi-même. Simple ont une variable qui est un entier. Lorsque vous commencez une requête, incrémentez le nombre. Quand on termine, décrémentez-le. Quand c'est zéro, il n'y a pas de demandes en cours, donc c'est fini.
$('#button').click(function() {
var inProgress = 0;
function handleBefore() {
inProgress++;
};
function handleComplete() {
if (!--inProgress) {
// do what's in here when all requests have completed.
}
};
$.ajax({
beforeSend: handleBefore,
complete: function () {
// whatever
handleComplete();
// whatever
}
});
$.ajax({
beforeSend: handleBefore,
complete: function () {
// whatever
handleComplete();
// whatever
}
});
$.ajax({
beforeSend: handleBefore,
complete: function () {
// whatever
handleComplete();
// whatever
}
});
});
il est intéressant de noter que depuis $.when
attend toutes les requêtes ajax comme arguments séquentiels (pas un tableau) vous verrez généralement $.when
utilisé avec .apply()
comme ainsi:
// Save all requests in an array of jqXHR objects
var requests = arrayOfThings.map(function(thing) {
return $.ajax({
method: 'GET',
url: 'thing/' + thing.id
});
});
$.when.apply(this, requests).then(function(resp1, resp2/*, ... */) {
// Each argument is an array with the following structure: [ data, statusText, jqXHR ]
var responseArgsArray = Array.prototype.slice.call(this, arguments);
});
c'est parce que $.when
accepte args comme ceci
$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);
et pas comme ceci:
$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);
Modifier -- peut-être que la meilleure option serait de créer un endpoint de service qui fait tout ce que les trois demandes font. De cette façon, vous ne devez faire qu'une seule demande, et toutes les données est là où vous avez besoin qu'il soit dans la réponse. Si vous constatez que vous faites les mêmes 3 requêtes encore et encore, vous voudrez probablement suivre cette voie. Il est souvent une bonne décision de conception de mettre en place un service de façade sur le serveur qui regroupe les actions généralement utilisées de plus petits serveurs. Juste une idée.
une façon de le faire serait de créer un "sync" objet dans votre gestionnaire de clic avant les appels ajax. Quelque chose comme
var sync = {
count: 0
}
la synchronisation sera liée à la portée des appels de succès automatiquement (fermeture). Dans le handler succès, vous incrémentez le compte, et s'il est 3 vous pouvez appeler l'autre fonction.
alternativement, vous pouvez faire quelque chose comme
var sync = {
success1Complete: false,
...
success3Complete: false,
}
lorsque chaque succès est exécuté, cela changerait la valeur de la synchronisation en true. Vous devez vérifier la synchronisation pour vous assurer que tous les trois sont vrais avant de continuer.
Notez le cas où un de vos xhrs ne retourne pas le succès -- vous devez en tenir compte.
encore une autre option serait de toujours appeler la fonction finale dans votre handlers de succès, et lui avoir accès à l'option de synchronisation pour déterminer s'il faut réellement faire quelque chose. Vous devez vous assurer que la synchronisation est dans la portée de cette fonction.
j'aime l'idée de hvgotcodes. Ma suggestion est d'ajouter un générique incrementer qui compare le nombre le nombre, puis exécute le dernier rappel. Cela pourrait être construit dans le dernier rappel.
var sync = {
callbacksToComplete = 3,
callbacksCompleted = 0,
addCallbackInstance = function(){
this.callbacksCompleted++;
if(callbacksCompleted == callbacksToComplete) {
doFinalCallBack();
}
}
};
[révisé pour refléter les mises à jour des noms.]
j'ai posé la même question il y a un certain temps et j'ai obtenu quelques bonnes réponses ici: meilleure façon d'ajouter un 'callback' après une série d'appels asynchrones XHR
j'ai obtenu quelques bons conseils des réponses sur cette page. J'ai adapté un peu pour mon utilisation et pensé que je pourrais partager.
// lets say we have 2 ajax functions that needs to be "synchronized".
// In other words, we want to know when both are completed.
function foo1(callback) {
$.ajax({
url: '/echo/html/',
success: function(data) {
alert('foo1');
callback();
}
});
}
function foo2(callback) {
$.ajax({
url: '/echo/html/',
success: function(data) {
alert('foo2');
callback();
}
});
}
// here is my simplified solution
ajaxSynchronizer = function() {
var funcs = [];
var funcsCompleted = 0;
var callback;
this.add = function(f) {
funcs.push(f);
}
this.synchronizer = function() {
funcsCompleted++;
if (funcsCompleted == funcs.length) {
callback.call(this);
}
}
this.callWhenFinished = function(cb) {
callback = cb;
for (var i = 0; i < funcs.length; i++) {
funcs[i].call(this, this.synchronizer);
}
}
}
// this is the function that is called when both ajax calls are completed.
afterFunction = function() {
alert('All done!');
}
// this is how you set it up
var synchronizer = new ajaxSynchronizer();
synchronizer.add(foo1);
synchronizer.add(foo2);
synchronizer.callWhenFinished(afterFunction);
il y a des limites ici, mais pour mon cas, c'était ok. J'ai aussi trouvé que pour les trucs plus avancés il y a aussi un plugin AOP (pour jQuery) qui pourrait être utile: http://code.google.com/p/jquery-aop /
je suis tombé sur ce problème aujourd'hui, et c'était ma tentative naïve avant de regarder la réponse acceptée.
<script>
function main() {
var a, b, c
var one = function() {
if ( a != undefined && b != undefined && c != undefined ) {
alert("Ok")
} else {
alert( "¬¬ ")
}
}
fakeAjaxCall( function() {
a = "two"
one()
} )
fakeAjaxCall( function() {
b = "three"
one()
} )
fakeAjaxCall( function() {
c = "four"
one()
} )
}
function fakeAjaxCall( a ) {
a()
}
main()
</script>
ce n'est pas jquery (et il semble que jquery ait une solution réalisable) mais juste comme une autre option....
j'ai eu des problèmes similaires en travaillant avec SharePoint web services - vous avez souvent besoin de tirer des données de plusieurs sources pour générer des entrées pour un seul processus.
pour le résoudre j'ai intégré ce genre de fonctionnalité dans ma bibliothèque d'abstraction AJAX. Vous pouvez facilement définir une requête qui déclenchera un ensemble de manipulateurs une fois terminée. Cependant, chaque requête peut être définie avec plusieurs appels http. Voici le composant (et la documentation détaillée):
cet exemple simple crée une requête avec trois appels et transmet ensuite cette information, dans l'ordre d'appel, à un seul gestionnaire:
// The handler function
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) };
// Create the pool
myPool = DP_AJAX.createPool();
// Create the request
myRequest = DP_AJAX.createRequest(AddUp);
// Add the calls to the request
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]);
// Add the request to the pool
myPool.addRequest(myRequest);
noter que contrairement à beaucoup d'autres solutions (y compris, je crois que le "quand" solution dans jquery) à condition que cette méthode ne force pas un seul threading des appels effectués - chacun continuera de fonctionner aussi rapidement (ou aussi lentement) que l'environnement le permet, mais le gestionnaire unique ne sera appelé que lorsque tous seront terminés. Il prend également en charge le réglage des valeurs de timeout et des tentatives de réessaiement si votre service est un peu flakey.
j'ai trouvé cela incroyablement utile (et incroyablement simple à comprendre du point de vue du code). Plus d'enchaînement, plus de comptage d'appels et d'économies sortie. Juste "régler et oublier".
async : false,
Par défaut, toutes les demandes sont envoyées de manière asynchrone (c'est à true par défaut). Si vous avez besoin de requêtes synchrones, définissez cette option à false
. Les requêtes "Cross-domain "et dataType: "jsonp"
ne prennent pas en charge le fonctionnement synchrone. Notez que les requêtes synchrones peuvent temporairement verrouiller le navigateur, désactivant toute action pendant que la requête est active. A partir de jQuery 1.8 , l'utilisation de async: false
avec jqXHR ( $.Deferred
) est déprécié; vous devez utiliser les options de rappel succès/erreur/complete au lieu des méthodes correspondantes de jqXHR objet tel que jqXHR.done()
ou le déprécié jqXHR.success()
.
$.ajax({type:'POST', url:'www.naver.com', dataType:'text', async:false,
complete:function(xhr, textStatus){},
error:function(xhr, textStatus){},
success:function( data ){
$.ajax({type:'POST',
....
....
success:function(data){
$.ajax({type:'POST',
....
....
}
}
});
je suis désolé mais je ne peux pas expliquer ce que j'ai à dire parce que je suis un coréen qui ne parle même pas un mot en anglais. mais je pense que vous pouvez comprendre facilement.