rappel jQuery pour plusieurs appels ajax

Je veux faire trois appels ajax dans un événement click. Chaque appel ajax effectue une opération distincte et renvoie les données nécessaires à un dernier rappel. Les appels eux-mêmes ne sont pas dépendants les uns des autres, ils peuvent tous aller en même temps, mais j’aimerais avoir un dernier rappel lorsque 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*/}); }); 

Voici un object de rappel que j’ai écrit où vous pouvez soit définir un seul rappel pour le déclencher une fois que tout est terminé, soit laisser chacun un rappel propre et le déclencher une fois tous terminés:

REMARQUER

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){},[...]); 

Exemple de différé ici

pour jQuery <1.5, ce qui suit fonctionnera ou si vous avez besoin que vos appels ajax soient déclenchés à des moments inconnus, comme illustré ici avec deux boutons: déclenché après avoir cliqué sur les deux boutons

[usage]

pour rappel unique une fois terminé: Exemple de travail

 // 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); } }); 

chacun ayant son propre rappel lorsque tout est terminé: Exemple de travail

 //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 des réponses à cela, mais je pense qu’il y a quelque chose à mentionner ici qui simplifiera grandement votre code. jQuery a introduit le $.when .when dans la v1.5. Ça ressemble à:

 $.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) { //this callback will be fired once all ajax calls have finished. }); 

Je ne l’ai pas vu mentionné ici, j’espère que ça aide.

Ne voyant pas la nécessité d’un object malwares moi-même. Simple a une variable qui est un entier. Lorsque vous lancez une demande, incrémentez le nombre. Quand on termine, décrémentez-le. Lorsqu’il est à zéro, aucune demande n’est en cours, vous avez donc terminé.

 $('#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 ceci:

 // 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 des arguments comme celui-ci

 $.when(ajaxRequest1, ajaxRequest2, ajaxRequest3); 

Et pas comme ça:

 $.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]); 

EDIT – peut-être que la meilleure option serait de créer un sharepoint terminaison de service qui fait tout ce que font les trois requêtes. De cette manière, il vous suffit de faire une seule demande et toutes les données sont là où vous en avez besoin dans la réponse. Si vous trouvez que vous faites les mêmes 3 demandes encore et encore, vous voudrez probablement aller dans cette voie. C’est souvent une bonne décision de conception de mettre en place un service de façade sur le serveur qui regroupe généralement de plus petites actions de serveur. Juste une idée.


Une façon de le faire serait de créer un object ‘sync’ dans votre gestionnaire de clics avant les appels ajax. Quelque chose comme

 var sync = { count: 0 } 

La synchronisation sera automatiquement liée à la scope des appels de réussite (clôture). Dans le gestionnaire de réussite, vous incrémentez le nombre, et si c’est 3, vous pouvez appeler l’autre fonction.

Alternativement, vous pourriez faire quelque chose comme

 var sync = { success1Complete: false, ... success3Complete: false, } 

Lorsque chaque succès est exécuté, la valeur de la synchronisation est définie sur true. Vous devrez vérifier la synchronisation pour vous assurer que tous les trois sont vrais avant de continuer.

Notez le cas où l’un de vos xhrs ne renvoie pas de succès – vous devez en tenir compte.

Une autre option serait de toujours appeler la fonction finale dans vos gestionnaires de succès et de lui faire accéder à l’option de synchronisation pour déterminer s’il faut réellement faire quelque chose. Cependant, vous devez vous assurer que la synchronisation est dans la scope de cette fonction.

J’aime l’idée de hvgotcodes. Ma suggestion est d’append un incrémenteur générique qui compare le nombre complet au nombre requirejs, puis exécute le rappel final. Cela pourrait être intégré dans le rappel final.

 var sync = { callbacksToComplete = 3, callbacksCompleted = 0, addCallbackInstance = function(){ this.callbacksCompleted++; if(callbacksCompleted == callbacksToComplete) { doFinalCallBack(); } } }; 

[Modifié pour refléter les mises à jour des noms.]

J’ai posé la même question il y a quelque temps et j’ai reçu quelques bonnes réponses ici: Meilleur moyen d’append un “rappel” après une série d’appels XHR asynchrones

J’ai eu quelques bons conseils à partir des réponses sur cette page. Je l’ai un peu adapté pour mon usage et j’ai pensé pouvoir 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 limitations ici, mais pour mon cas, ça allait. J'ai aussi trouvé que pour les choses plus avancées, 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.

  

Ce n’est pas jquery (et il semble que jquery a une solution viable) mais juste comme une autre option ….

J’ai eu des problèmes similaires en travaillant lourdement avec les services Web SharePoint – vous devez souvent extraire 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 type de fonctionnalité dans ma bibliothèque d’abstraction AJAX. Vous pouvez facilement définir une requête qui déclenchera un ensemble de gestionnaires une fois terminé. Cependant, chaque requête peut être définie avec plusieurs appels http. Voici le composant (et la documentation détaillée):

DPAJAX sur DepressedPress.com

Cet exemple simple crée une requête avec trois appels, puis transmet 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); 

Notez que contrairement à beaucoup d’autres solutions (y compris, je crois, la solution “lorsque” dans jquery) à condition que cette méthode ne force pas le threading unique des appels en cours – chacun fonctionnera toujours aussi rapidement (ou lentement) que l’environnement le permet mais le gestionnaire unique ne sera appelé que lorsque tous seront terminés. Il prend également en charge la définition des valeurs de délai d’attente et réessaie si votre service est un peu flou.

Je l’ai trouvé incroyablement utile (et incroyablement simple à comprendre du sharepoint vue du code). Plus de chaînage, plus de comptage d’appels et de sauvegarde. Il suffit de régler et oublier”.

 async : false, 

Par défaut, toutes les demandes sont envoyées de manière asynchrone (c’est-à-dire que cette valeur est définie sur true par défaut). Si vous avez besoin de requêtes synchrones, définissez cette option sur false . Requêtes dataType: "jsonp" et requêtes dataType: "jsonp" ne prennent pas en charge le fonctionnement synchrone. Notez que les requêtes synchrones peuvent verrouiller temporairement le navigateur, désactivant toute action lorsque la demande est active. À partir de jQuery 1.8 , l’utilisation de l’ async: false avec jqXHR ( $.Deferred ) est obsolète; Vous devez utiliser les options de rappel succès / erreur / complet au lieu des méthodes correspondantes de l’object jqXHR telles que jqXHR.done() ou jqXHR.success() obsolète.

 $.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 je veux dire parce que je suis un Coréen qui ne peut même pas parler un mot en anglais. mais je pense que vous pouvez facilement le comprendre.