143 votes

Callback jQuery 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 des données qui sont nécessaires pour un appel final. Les appels eux-mêmes ne sont pas dépendants les uns des autres, ils peuvent tous être lancés en même temps, mais je voudrais avoir un rappel final lorsque 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*/});
});

166voto

Greg Points 1388

Il semble que vous ayez trouvé certaines réponses à cette question, mais je pense qu'il y a quelque chose qui vaut la peine d'être mentionné ici et qui simplifiera grandement votre code. jQuery a introduit la fonction $.when dans la v1.5. Ça ressemble à ça :

$.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 aidera.

7 votes

Merci pour la réponse, c'est définitivement la voie à suivre pour moi. Il utilise jQuery, il est compact, court et expressif.

3 votes

La réponse de Subhaze est géniale mais vraiment, celle-ci est la bonne.

10 votes

Je suis d'accord pour dire que c'est une bonne solution quand on a jQuery 1.5+ mais au moment de la réponse la fonctionnalité différée n'était pas disponible :(

118voto

subhaze Points 4903

Voici un objet de rappel que j'ai écrit et dans lequel vous pouvez soit définir un seul rappel à lancer une fois que tout est terminé, soit laisser chaque personne avoir son propre rappel et le lancer 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){},[...]);

Exemple de report 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 ici avec deux boutons : déclenché après que les deux boutons aient été cliqués

[utilisation]

pour simple callback 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 leur propre callback 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);
        };
    };
})();

1 votes

Merci pour cet excellent exemple de code ! Je pense que je vais pouvoir me débrouiller avec le reste. Merci encore !

0 votes

Pas de problème, content que ça ait aidé ! Je me suis un peu emporté avec le projet :P J'ai encore quelques idées pour le faire. Je prévois de le mettre à jour pour que vous n'ayez pas à spécifier un nombre de requêtes à l'initialisation.

0 votes

Joli, j'espère que vous le ferez ! Très intéressant, je suggérerais d'ajouter l'option d'ajouter un callback supplémentaire. Je l'ai fait et cela fonctionne parfaitement pour ce que je veux faire. Merci encore !

15voto

Matt Points 38395

Je ne vois pas l'utilité d'un objet malicieux. Il suffit d'avoir une variable qui est un nombre entier. Quand vous lancez une requête, incrémentez le nombre. Quand une demande est terminée, on la décrémente. Quand il est à zéro, il n'y a pas de demande en cours, donc vous avez 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
        }
    });
});

0 votes

Ceci est très simple et fonctionne parfaitement... Pourriez-vous expliquer un peu mieux ce qui se passe dans if (--inProgress) ? Il devrait contrôler si la variable inProgress == 0 et lui soustraire une unité mais je ne comprends pas la syntaxe compacte...

1 votes

@Pitto : Oups... en fait, il y a une erreur de logique, et ça devrait être... if (!--inProgress) . La version correcte est analogue à inProgress = inProgress - 1; if (inProgress == 0) { /* do stuff */ } . La forme compacte combine la opérateur de décrémentation du préfixe ( -- ) et l'opérateur de négation ( ! ) pour qu'elle soit évaluée à "vrai" (et donc pour qu'elle exécute la commande if ), si la décrémentation inProgress résulte en inProgress == 0 et évalue à "false" (et donc ne fait rien) sinon.

0 votes

Tout simplement merveilleux ! Merci pour votre temps et votre patience.

4voto

Adolph Trudeau Points 218

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

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

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

0 votes

Je suis d'accord, ça m'a aidé à mieux comprendre ce dont j'avais réellement besoin.

3voto

hvgotcodes Points 55375

EDIT -- peut-être que la meilleure option serait de créer un point de terminaison de service qui fait tout ce que les trois requêtes font. De cette façon, vous n'avez qu'une seule requête à faire, et toutes les données se trouvent là où vous en avez besoin dans la réponse. Si vous trouvez que vous faites les 3 mêmes requêtes encore et encore, vous voudrez probablement suivre cette voie. Il est souvent judicieux de mettre en place un service de façade sur le serveur qui regroupe les petites actions serveur couramment utilisées. C'est juste une idée.


Une façon de le faire serait de créer un objet "sync" dans votre gestionnaire de clics 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 gestionnaire de succès, vous incrémentez le compte, et s'il est de 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é, cela changerait la valeur dans le sync à true. Vous devrez vérifier la synchronisation pour vous assurer que les trois valeurs sont vraies avant de poursuivre.

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

Une autre option encore 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 effectivement faire quelque chose. Vous devrez cependant vous assurer que la synchronisation se trouve dans la portée de cette fonction.

0 votes

J'aime plus votre première suggestion pour quelques raisons. Chaque demande a un but distinct, donc j'aimerais les garder séparées pour cette raison. En outre, les demandes sont actuellement dans une fonction parce que je les utilise ailleurs, donc j'ai essayé de garder un design modulaire si possible. Merci pour votre aide !

0 votes

@Jisaak, oui, mettre une méthode sur le serveur pour gérer cela pourrait ne pas avoir de sens pour vous. Mais il est acceptable de mettre en place une façade sur le serveur. Vous parlez de modularité, créer une méthode qui gère les trois choses est modulaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X