JQuery se débarrasse des fonctions anjax imbriquées

Dans mon JS, je dois avoir le contenu de 3 fichiers avec AJAX, puis faire un certain code. Cela a conduit à une création plutôt étrange des fonctions asynchrones imbriquées. Aussi, chaque fois que je travaille avec des fonctions asynchrones, cette nidification laide vient.

Comment puis-je éviter les fonctions de nidification lorsque je souhaite vraiment les attendre pour chacune d'elles? (J'utilise jQuery si cela aide)

function loadFilesAndDoStuff() { $.get(firstFile, function(first_file_data) { $.get(secondFile, function(second_file_data) { $.get(thirdFile, function(third_file_data) { someOtherAsyncFunction(function(combined_file_data) { // do some stuff with the "combined_file_data". }); }); }); }); } 

Voici plusieurs techniques différentes avec et sans utilisation de différé. Dans tous les cas, tous les appels ajax sont lancés, puis une partie du code permet de suivre le moment où tous les appels ajax ont été complétés et recueille les données des appels au fur et à mesure qu'ils sont terminés. Lorsque la dernière complète toutes les données est disponible.

Vous pouvez lancer tous les trois appels ajax à la fois et vérifier chaque fonction d'achèvement si elles sont toutes terminées, stocker les résultats dans une variable locale jusqu'à ce qu'ils soient tous terminés:

 function loadFilesAndDoStuff() { var cntr = 3; var data1, data2, data3; function checkDone() { --cntr; if (cntr === 0) { // all three are done here someOtherFunction(combined_file_data); } } $.get(firstFile, function(data) { data1 = data; checkDone(); }); $.get(secondFile, function(data) { data2 = data; checkDone(); }); $.get(thirdFile, function(data) { data3 = data; checkDone(); }); } 

Ou, vous pouvez en mettre plus dans une fonction commune et passer le tableau des noms de fichiers à la fonction:

 function loadFilesAndDoStuff(filesArray) { var results = []; var doneCnt = 0; function checkDone(index, data) { results[index] = data; ++doneCnt; if (doneCnt === filesArray.length) { // all results are in the results array now } } for (var i = 0; i < filesArray.length; i++) { results.push(null); $.get(filesArray[i], checkDone.bind(this, i)); } } 

En utilisant Différés, vous pouvez le faire:

 function loadFilesAndDoStuff(filesArray) { var results = []; var deferreds = []; function doneOne(index, data) { results[index] = data; } for (var i = 0; i < filesArray.length; i++) { results.push(null); deferreds.push($.get(filesArray[i], doneOne.bind(this, i))); } $.when.apply($, deferreds).done(function() { // all ajax calls are done and results are available now }); } 

Ou, une version encore plus courte en utilisant le fait que les reports ont sauvegardé les arguments des gestionnaires de succès pour chaque report:

 function loadFilesAndDoStuff(filesArray) { var deferreds = []; for (var i = 0; i < filesArray.length; i++) { deferreds.push($.get(filesArray[i])); } $.when.apply($, deferreds).done(function() { // all ajax calls are done and results are available now // arguments[0][0] is the data from the first $.get call // arguments[1][0] is the data from the second $.get call // and so on }); } 

Démo de travail de cette dernière option: http://jsfiddle.net/jfriend00/5ppU4/

FYI, il n'y a pas de magie à l'intérieur de $.when() . Si vous regardez le code jQuery pour cela, il suffit de garder un compteur lorsque les arguments qui lui sont transmis sont tous terminés (similaire à mes deux premières options ici). La principale différence est qu'il utilise l'interface de promesse avec l'objet jqXHR au lieu de savoir qu'il s'agit d'un appel ajax. Mais conceptuellement, il fait la même chose.


Voici un autre en utilisant un nouvel objet que j'ai écrit pour gérer plusieurs reports.

 function loadFilesAndDoStuff(filesArray) { var deferred = $.MultiDeferred().done(function() { // all ajax calls are done and results are available now // arguments[0][0] is the data from the first $.get call // arguments[1][0] is the data from the second $.get call // and so on }); for (var i = 0; i < filesArray.length; i++) { deferred.add($.get(filesArray[i])); } } 

Le code MultiDeferred est un plug-in jQuery spécifiquement écrit pour traiter votre notification lorsque plusieurs ajouts sont terminés et que le code est ici:

 jQuery.MultiDeferred = function(/* zero or more promises */) { // make the Deferred var self = jQuery.Deferred(); var remainingToFinish = 0; var promises = []; var args = []; var anyFail = false; var failImmediate = false; function _add(p) { // save our index in a local variable so it's available in this closure later var index = promises.length; // save this promise promises.push(p); // push placeholder in the args array args.push([null]); // one more waiting to finish ++remainingToFinish; // see if all the promises are done function checkDone(fail) { return function() { anyFail |= fail; // make copy of arguments so we can save them args[index] = Array.prototype.slice.call(arguments, 0); --remainingToFinish; // send notification that one has finished self.notify.apply(self, args[index]); // if all promises are done, then resolve or reject if (self.state() === "pending" && (remainingToFinish === 0 || (fail && failImmediate))){ var method = anyFail ? "reject" : "resolve"; self[method].apply(self, args); } } } // add our own monitors so we can collect all the data // and keep track of whether any fail p.done(checkDone(false)).fail(checkDone(true)); } // add a new promise self.add = function(/* one or more promises or arrays of promises */) { if (this.state() !== "pending") { throw "Can't add to a deferred that is already resolved or rejected"; } for (var i = 0; i < arguments.length; i++) { if (arguments[i] instanceof Array) { for (var j = 0; j < arguments[i].length; j++) { _add(arguments[i][j]); } } else { _add(arguments[i]); } } return this; } // get count of remaining promises that haven't completed yet self.getRemaining = function() { return remainingToFinish; } // get boolean on whether any promises have failed yet self.getFailYet = function() { return anyFail; } self.setFailImmediate = function(failQuick) { failImmediate = failQuick; return this; } // now process all the arguments for (var i = 0; i < arguments.length; i++) { self.add(arguments[i]); } return self; }; 

Créez un tableau de chaque fichier nécessaire, puis faites passer le tableau de fichiers et appelez $.get chaque itération, et $.get -lui d'appeler une fonction de combinaison qui combine les données et effectuez une vérification de compte, une fois que le compte est atteint, le rappel d'appel.

 function loadData(files,callback){ var combinedData = ""; var count = 0; function combineFile(data){ count++; combinedData += data; if(count==files.length-1){ callback(combinedData); } } for(var i=0; i<files.length; i++){ $.get(files[i],combineFile); } } loadData(["files1.txt","files2.txt","files3.txt"],function(data){ console.log("Combined Data: ",data); });