Comment puis-je attendre le jeu de fonctions de rappel asynchrone?

J'ai un code qui ressemble à ceci en javascript:

forloop { //async call, returns an array to its callback } 

Après que TOUS ces appels asynchrones sont terminés, je veux calculer la min. Sur tous les tableaux.

Comment puis-je les attendre à tous?

Ma seule idée en ce moment est d'avoir un ensemble de booléens appelé terminé, et de définir [i] à vrai dans la ième fonction de rappel, puis de dire pendant que (tout n'est pas fait) {}

Edit: Je suppose qu'une solution possible, mais laide, serait de modifier le tableau fait dans chaque rappel, puis appeler une méthode si tous les autres sont définis à partir de chaque rappel, le dernier rappel à terminer appellera la méthode continue.

Merci d'avance.

Vous n'avez pas été très précis avec votre code, donc je vais faire un scénario. Disons que vous avez 10 appels ajax et que vous souhaitez accumuler les résultats de ces 10 appels ajax et ensuite, lorsqu'ils ont terminé, vous voulez faire quelque chose. Vous pouvez le faire de la sorte en accumulant les données dans un tableau et en gardant la trace lorsque le dernier a terminé:

Compteur manuel

 var ajaxCallsRemaining = 10; var returnedData = []; for (var i = 0; i < 10; i++) { doAjax(whatever, function(response) { // success handler from the ajax call // save response returnedData.push(response); // see if we're done with the last ajax call --ajaxCallsRemaining; if (ajaxCallsRemaining <= 0) { // all data is here now // look through the returnedData and do whatever processing // you want on it right here } }); } 

Remarque: la gestion des erreurs est importante ici (non représentée car elle est spécifique à la façon dont vous effectuez vos appels ajax). Vous voudrez réfléchir à la façon dont vous allez gérer le cas lorsqu'un appel ajax ne se termine jamais, soit par une erreur, soit par une longue période ou après une longue période.


JQuery promet

En ajoutant à ma réponse en 2014. Ces jours-ci, les promesses sont souvent utilisées pour résoudre ce type de problème puisque $.ajax() jQuery renvoie déjà une promesse et $.when() fera savoir quand un groupe de promesses sont résolues et Recueillera les résultats de retour pour vous:

 var promises = []; for (var i = 0; i < 10; i++) { promises.push($.ajax(...)); } $.when.apply($, promises).then(function() { // returned data is in arguments[0][0], arguments[1][0], ... arguments[9][0] // you can process it here }, function() { // error occurred }); 

ES6 Promesses standard

Comme spécifié dans la réponse de kba : si vous avez un environnement avec des promesses natives intégrées (navigateur moderne ou node.js ou utilisant babeljs transpile ou utilisant un polyfill prometteur), vous pouvez utiliser les promesses spécifiées ES6. Voir ce tableau pour le support du navigateur. Les promesses sont prises en charge dans presque tous les navigateurs actuels, sauf IE.

Si doAjax() renvoie une promesse, vous pouvez le faire:

 var promises = []; for (var i = 0; i < 10; i++) { promises.push(doAjax(...)); } Promise.all(promises).then(function() { // returned data is in arguments[0], arguments[1], ... arguments[n] // you can process it here }, function(err) { // error occurred }); 

Si vous devez faire une opération asynchique non prometteuse en une qui renvoie une promesse, vous pouvez la «promettre» comme ceci:

 function doAjax(...) { return new Promise(function(resolve, reject) { someAsyncOperation(..., function(err, result) { if (err) return reject(err); resolve(result); }); }); } 

Et, puis utilisez le modèle ci-dessus:

 var promises = []; for (var i = 0; i < 10; i++) { promises.push(doAjax(...)); } Promise.all(promises).then(function() { // returned data is in arguments[0], arguments[1], ... arguments[n] // you can process it here }, function(err) { // error occurred }); 

Bluebird Promises

Si vous utilisez une bibliothèque plus dotée d'une fonctionnalité telle que la bibliothèque Bluebird prometteur , il comporte certaines fonctionnalités supplémentaires conçues pour faciliter cette tâche:

  var doAjax = Promise.promisify(someAsync); var someData = [...] Promise.map(someData, doAjax).then(function(results) { // all ajax results here }, function(err) { // some error here }); 

Vous pouvez l'imiter comme ceci:

  countDownLatch = { count: 0, check: function() { this.count--; if (this.count == 0) this.calculate(); }, calculate: function() {...} }; 

Alors chaque appel asynchrone fait ceci:

 countDownLatch.count++; 

Alors que dans chaque appel d'asynchrone à la fin de la méthode, vous ajoutez cette ligne:

 countDownLatch.check(); 

En d'autres termes, vous imitez une fonctionnalité de verrouillage à rebours.

Vous pouvez utiliser l'objet différé de jQuery avec la méthode When.

 deferredArray = []; forloop { deferred = new $.Deferred(); ajaxCall(function() { deferred.resolve(); } deferredArray.push(deferred); } $.when(deferredArray, function() { //this code is called after all the ajax calls are done }); 

Vérification à partir de 2015: nous avons maintenant des promesses natives dans le navigateur le plus récent (Edge 12, Firefox 40, Chrome 43, Safari 8, Opera 32 et Android 4.4.4 et iOS Safari 8.4, mais pas Internet Explorer, Opera Mini et anciennes versions D'Android).

Si nous voulons effectuer 10 actions asynchrones et nous les notifier lorsqu'elles ont fini, nous pouvons utiliser Promise.all , sans bibliothèques externes:

 function asyncAction(i) { return new Promise(function(resolve, reject) { var result = calculateResult(); if (result.hasError()) { return reject(result.error); } return resolve(result); }); } var promises = []; for (var i=0; i < 10; i++) { promises.push(asyncAction(i)); } Promise.all(promises).then(function AcceptHandler(results) { handleResults(results), }, function ErrorHandler(error) { handleError(error); }); 

C'est la manière la plus soignée à mon avis.

Promise.all

FetchAPI

(Pour une raison quelconque, Array.map ne fonctionne pas dans .then fonctions pour moi. Mais vous pouvez utiliser un .forEach et [] .concat () ou quelque chose de similaire)

 Promise.all([ fetch('/user/4'), fetch('/user/5'), fetch('/user/6'), fetch('/user/7'), fetch('/user/8') ]).then(responses => { return responses.map(response => {response.json()}) }).then((values) => { console.log(values); }) 

Utilisez une bibliothèque de flux de contrôle comme after

 after.map(array, function (value, done) { // do something async setTimeout(function () { // do something with the value done(null, value * 2) }, 10) }, function (err, mappedArray) { // all done, continue here console.log(mappedArray) })