Appeler une fonction asynchrone dans une boucle for en JavaScript

J'ai le code suivant:

for(var i = 0; i < list.length; i++){ mc_cli.get(list[i], function(err, response) { do_something(i); }); } 

mc_cli est une connexion à une base de données memchached. Comme vous pouvez l'imaginer, la fonction de rappel est asynchrone, donc elle peut être exécutée lorsque la boucle for terminée. De plus, lorsque vous appelez de cette façon, do_something(i) il utilise toujours la dernière valeur de la boucle for.

J'ai essayé avec une fermeture de cette façon

 do_something((function(x){return x})(i)) 

Mais apparemment cela utilise de nouveau toujours la dernière valeur de l'index de la boucle for.

J'ai également essayé de déclarer une fonction avant la boucle for like like:

 var create_closure = function(i) { return function() { return i; } } 

Puis appeler

 do_something(create_closure(i)()) 

Mais encore sans succès, la valeur de retour étant toujours la dernière valeur de la boucle for.

Quelqu'un peut-il me dire ce que je fais mal avec les fermetures? Je pensais les avoir compris, mais je ne peux pas comprendre pourquoi cela ne fonctionne pas.

Puisque vous utilisez un tableau, vous pouvez simplement utiliser pour forEach ce qui fournit l'élément de la liste et l'index dans le rappel. L'itération aura sa propre portée.

 list.forEach(function(listItem, index){ mc_cli.get(listItem, function(err, response) { do_something(index); }); }); 

C'est le paradigme de la fonction asynchrone à l'intérieur de la boucle, et je m'occupe habituellement d'elle en utilisant une fonction anonyme anormale. Cela garantit que les fonctions asynchrones sont appelées avec la valeur correcte de la variable d'index.

D'accord! Super. Donc, toutes les fonctions asynchrones ont été démarrées et la boucle sort. Maintenant, on ne sait pas quand ces fonctions seront complétées, en raison de leur nature asynchrone, ou dans quel ordre elles seront complétées. Si vous avez un code qui doit attendre que toutes ces fonctions aient été complétées avant d'exécuter, je recommande de garder un nombre simple de combien de fonctions ont terminé:

 var total = parsed_result.list.length; var count = 0; for(var i = 0; i < total; i++){ (function(foo){ mc_cli.get(parsed_result.list[foo], function(err, response) { do_something(foo); count++; if (count > total - 1) done(); }); }(i)); } // You can guarantee that this function will not be called until ALL of the // asynchronous functions have completed. function done() { console.log('All data has been loaded :).'); } 

Vous étiez assez proche, mais vous devriez passer la fermeture pour get au lieu de le mettre dans le rappel:

 function createCallback(i) { return function(){ do_something(i); } } for(var i = 0; i < list.length; i++){ mc_cli.get(list[i], createCallback(i)); } 

Je sais que c'est un vieux fil mais, de toute façon, j'ajoute ma réponse. ES2015 let a la fonctionnalité de relayer la variable de boucle sur chaque itération, de sorte qu'elle maintient la valeur de la variable de boucle dans les rappels asynchrones, de sorte que vous pouvez essayer la suivante:

 for(let i = 0; i < list.length; i++){ mc_cli.get(list[i], function(err, response) { do_something(i); }); } 

Mais de toute façon, il est préférable d'utiliser pour forEach ou créer une fermeture en utilisant la fonction d'appel immédiat, car la fonctionnalité est ES2015 et peut-être ne pas supporter tous les navigateurs et les implémentations. À partir de ici, sous Bindings ->let->for/for-in loop iteration scope je vois qu'il n'est pas pris en charge jusqu'à Edge 13 et même jusqu'à Firefox 49 (je n'ai pas vérifié dans ces navigateurs). Il dit même qu'il n'est pas pris en charge avec le nœud 4, mais j'ai personnellement testé et il semble qu'il soit pris en charge.