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*/});
});
115
demandé sur mandza 2010-12-06 20:02:39

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

exemple de différé ici

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);
        };
    };
})();
94
répondu subhaze 2012-12-11 18:18:34

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.

136
répondu Greg 2012-11-26 22:20:49

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
        }
    });
});
8
répondu Matt 2015-04-28 13:41:55

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]);
7
répondu Cory Danielson 2016-06-02 04:42:34

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.

3
répondu hvgotcodes 2010-12-06 19:54:32

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.]

3
répondu Adolph Trudeau 2010-12-06 20:06:19

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

0
répondu SBUJOLD 2017-05-23 11:47:19

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 /

0
répondu PålOliver 2011-01-10 08:52:26

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>
0
répondu OscarRyz 2011-11-16 02:33:25

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):

DPAJAX at DepressedPress.com

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".

0
répondu Jim Davis 2012-09-08 15:15:48
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() .

-1
répondu Esteves Klein 2013-01-07 00:49:32
$.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.

-2
répondu nicecue 2010-12-06 17:53:16