web-dev-qa-db-fra.com

rappel 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 les données nécessaires pour un rappel final. Les appels eux-mêmes ne sont pas dépendants les uns des autres, ils peuvent tous être passés en même temps, mais j'aimerais un dernier rappel 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*/});
});
116
MisterIsaak

Voici un objet de rappel que j'ai écrit dans lequel vous pouvez soit définir un seul rappel pour déclencher une fois que tout est terminé, soit laisser chacun avoir son propre rappel et les renvoyer tous une fois terminé:

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 report ici

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

[utilisation]

pour le rappel single 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 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);
        };
    };
})();
97
subhaze

On dirait que vous avez des réponses à cela, mais je pense qu'il y a quelque chose d'intéressant à mentionner ici qui simplifiera grandement votre code. jQuery a introduit le $.when in 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, espérons que cela aide.

139
Greg

Ne voyant pas le besoin d'objet malarky 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. À 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
        }
    });
});
9
Matt

Il est à noter que, puisque $.when attend toutes les demandes ajax sous forme d'arguments séquentiels (pas un tableau), vous verrez généralement que $.when est utilisé avec .apply() comme suit:

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

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

Et pas comme ça:

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

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

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

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

4
Adolph Trudeau

EDIT - La meilleure option serait peut-être de créer un point de terminaison de service qui réponde à toutes les demandes. De cette façon, vous ne devez faire qu'une seule demande et toutes les données sont celles où vous en avez besoin dans la réponse. Si vous constatez que vous faites les mêmes 3 demandes encore et encore, vous voudrez probablement suivre cette voie. Il est souvent judicieux de concevoir un service de façade sur le serveur qui regroupe des actions de serveur plus petites couramment utilisées. 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 automatiquement liée à la portée des appels de succès (fermeture). Dans le gestionnaire de succès, vous incrémentez le nombre et, s'il est égal à 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 devient true. Vous devrez vérifier la synchronisation pour vous assurer que les trois sont vraies avant de continuer.

Notez le cas où l’un de vos xhr ne donne pas de succès - vous devez en rendre compte.

Une autre option consiste à toujours appeler la dernière fonction de vos gestionnaires de réussite et à lui faire accéder à l’option de synchronisation pour déterminer s’il faut ou non faire quoi que ce soit. Vous devrez cependant vous assurer que la synchronisation est dans les limites de cette fonction.

3
hvgotcodes

J'ai rencontré 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
OscarRyz

J'ai trouvé un moyen plus facile de le faire sans avoir besoin de méthodes supplémentaires pour organiser une file d'attente.

JS

$.ajax({
  type: 'POST',
  url: 'ajax1.php',
  data:{
    id: 1,
    cb:'method1'//declaration of callback method of ajax1.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method1'
  eval(cb+"(JSON.stringify(data));");//here we calling method1 and pass all data
  }
});


$.ajax({
  type: 'POST',
  url: 'ajax2.php',
  data:{
    id: 2,
    cb:'method2'//declaration of callback method of ajax2.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method2'
  eval(cb+"(JSON.stringify(data));");//here we calling method2 and pass all data
  }
});


//the callback methods
function method1(data){
//here we have our data from ajax1.php
alert("method1 called with data="+data);
//doing stuff we would only do in method1
//..
}

function method2(data){
//here we have our data from ajax2.php
alert("method2 called with data="+data);
//doing stuff we would only do in method2
//..
}

PHP (ajax1.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method1'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax1"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

PHP (ajax2.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method2'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax2"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>
0
medicalbird

Ok, c’est vieux, mais laissez-moi apporter ma solution :)

function sync( callback ){
    syncCount--;
    if ( syncCount < 1 ) callback();
}
function allFinished(){ .............. }

window.syncCount = 2;

$.ajax({
    url: 'url',
    success: function(data) {
        sync( allFinished );
    }
});
someFunctionWithCallback( function(){ sync( allFinished ); } )

Cela fonctionne aussi avec les fonctions qui ont un rappel. Vous définissez le syncCount et vous appelez la fonction sync (...) dans le rappel de chaque action.

0
sic

J'ai posé la même question il y a quelque temps et j'ai obtenu quelques bonnes réponses ici: Le meilleur moyen d'ajouter un "rappel" après une série d'appels XHR asynchrones

0
SBUJOLD

J'ai eu quelques bons conseils des réponses sur cette page. Je l'ai adapté un peu à 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 quelques limitations ici, mais dans mon cas c'était correct ... J'ai aussi constaté que pour les choses plus avancées, il y avait aussi un plugin AOP (pour jQuery) qui pourrait être utile: http://code.google .com/p/jquery-aop/

0
PålOliver

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 avec les services Web SharePoint. Nous avons souvent besoin d'extraire des données de plusieurs sources pour générer des entrées pour un processus unique.

Pour résoudre ce problème, j'ai intégré ce type de fonctionnalité dans ma bibliothèque d'abstraction AJAX. Vous pouvez facilement définir une demande qui déclenchera un ensemble de gestionnaires une fois l’opération terminée. Cependant, chaque demande 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 demande avec trois appels, puis transmet cette information, dans l'ordre des appels, à 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 à la plupart des autres solutions (y compris, je crois, la solution "when" dans jQuery), à condition que cette méthode ne force pas le threading unique des appels en cours, chacune fonctionnera toujours 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 la définition de valeurs de délai et de nouvelles tentatives si votre service est un peu floconneux.

Je l'ai trouvé incroyablement utile (et incroyablement simple à comprendre du point de vue du code). Pas plus de chaînage, plus de comptage des appels et enregistrement de la sortie. Il suffit de régler et oublier".

0
Jim Davis