web-dev-qa-db-fra.com

Attente de plusieurs appels asynchrones pour terminer avant de continuer

Donc, j'ai une page qui se charge et via jquery.get fait plusieurs demandes pour remplir les listes déroulantes avec leurs valeurs.

$(function() {
    LoadCategories($('#Category'));
    LoadPositions($('#Position'));
    LoadDepartments($('#Department'));

    LoadContact();
};

Il appelle ensuite LoadContact (); Ce qui fait un autre appel, et quand il revient, il remplit tous les champs du formulaire. Le problème est que souvent, les listes déroulantes ne sont pas toutes remplies et, par conséquent, elles ne peuvent pas les définir sur la valeur correcte.

Ce que je dois être en mesure de faire, c'est que LoadContact ne s'exécute que lorsque les autres méthodes sont terminées et que les rappels sont exécutés.

Mais, je ne veux pas avoir à mettre un tas d'indicateurs à la fin des rappels de population déroulante, que je vérifie ensuite, et je dois avoir une vérification d'appel récursive setTimeout, avant d'appeler LoadContact ();

Y a-t-il quelque chose dans jQuery qui me permet de dire: "Exécutez ceci, quand tout cela est fait."?

Plus d'infos Je pense à quelque chose dans ce sens

$().executeAfter(
    function () {   // When these are done
        LoadCategories($('#Category'));
        LoadPositions($('#Position'));
        LoadDepartments($('#Department'));
    },
    LoadContact // Do this
);

... il faudrait garder une trace des appels ajax qui se produisent pendant l'exécution des méthodes, et quand ils sont tous terminés, appeler LoadContact;

Si je savais comment intercepter ajax qui sont faites dans cette fonction, je pourrais probablement écrire une extension jQuery pour ce faire.

Ma solution

;(function($) {
    $.fn.executeAfter = function(methods, callback) {

        var stack = [];

        var trackAjaxSend = function(event, XMLHttpRequest, ajaxOptions) {
            var url = ajaxOptions.url;

            stack.Push(url);
        }

        var trackAjaxComplete = function(event, XMLHttpRequest, ajaxOptions) {
            var url = ajaxOptions.url;

            var index = jQuery.inArray(url, stack);

            if (index >= 0) {
                stack.splice(index, 1);
            }

            if (stack.length == 0) {
                callback();
                $this.unbind("ajaxComplete");
            }
        }

        var $this = $(this);

        $this.ajaxSend(trackAjaxSend)
        $this.ajaxComplete(trackAjaxComplete)

        methods();
        $this.unbind("ajaxSend");
    };
})(jQuery);

Cela se lie à l'événement ajaxSend lors de l'appel des méthodes et conserve une liste des URL (besoin d'un meilleur identifiant unique) qui sont appelées. Il se délie ensuite d'ajaxSend, de sorte que seules les demandes qui nous intéressent sont suivies. Il se lie également à ajaxComplete et supprime les éléments de la pile à leur retour. Lorsque la pile atteint zéro, elle exécute notre rappel et dissocie l'événement ajaxComplete.

55
CaffGeek

Vous pouvez utiliser .ajaxStop() comme ceci:

$(function() {
  $(document).ajaxStop(function() {
    $(this).unbind("ajaxStop"); //prevent running again when other calls finish
    LoadContact();
  });
  LoadCategories($('#Category'));
  LoadPositions($('#Position'));
  LoadDepartments($('#Department'));
});

Cela s'exécutera lorsque toutes les demandes en cours seront terminées, puis se déliera pour ne pas s'exécuter si les futurs appels ajax dans la page s'exécutent. Assurez-vous également de le mettre avant vos appels ajax, afin qu'il soit lié assez tôt, c'est plus important avec .ajaxStart() , mais la meilleure pratique est de le faire avec les deux.

43
Nick Craver

Développer sur réponse de Tom Lianza , $.when() est maintenant une bien meilleure façon d'accomplir cela que d'utiliser .ajaxStop().

La seule mise en garde est que vous devez vous assurer que les méthodes asynchrones dont vous avez besoin pour attendre un retour Deferred object . Heureusement, les appels ajax jQuery le font déjà par défaut. Donc, pour implémenter le scénario à partir de la question, les méthodes qui doivent être attendues ressembleraient à ceci:

function LoadCategories(argument){
    var deferred = $.ajax({
       // ajax setup
    }).then(function(response){ 
       // optional callback to handle this response 
    });
    return deferred;
}

Ensuite, pour appeler LoadContact () après que les trois appels ajax sont retournés et ont éventuellement exécuté leurs propres rappels individuels:

// setting variables to emphasize that the functions must return deferred objects
var deferred1 = LoadCategories($('#Category'));
var deferred2 = LoadPositions($('#Position'));
var deferred3 = LoadDepartments($('#Department'));

$.when(deferred1, deferred2, deferred3).then(LoadContact);
17
KPD

Si vous utilisez Jquery 1.5 ou une version ultérieure, je soupçonne que l'objet différé est votre meilleur choix: http://api.jquery.com/category/deferred-object/

La méthode d'assistance, quand, est également assez agréable: http://api.jquery.com/jQuery.when/

7
Tom Lianza

Mais, je ne veux pas avoir à mettre un tas d'indicateurs à la fin des rappels de population déroulante, que je vérifie ensuite, et je dois avoir une vérification d'appel setTimeout récursive, avant d'appeler LoadContact () ;
Pas besoin de setTimeout. Vous vérifiez simplement dans chaque rappel que les trois listes sont remplies (ou mieux configurez un compteur, augmentez-le dans chaque rappel et attendez qu'il soit égal à 3), puis appelez LoadContact à partir du rappel. Cela me semble assez facile.

l'approche ajaxStop pourrait fonctionner, je ne la connais pas très bien.

0
Nikita Rybak