Reprise de la queue dans NodeJS

Donc, j'ai récemment rencontré un cas où je devais écrire un code où le rappel téléphonique lui-même et ainsi de suite et se demandait au sujet de NodeJS et du support d'appel arrière, alors j'ai trouvé cette réponse https://stackoverflow.com/a/30369729 en disant que oui, il est pris en charge .

Je l'ai donc essayé avec ce code simple:

"use strict"; function fac(n){ if(n==1){ console.trace(); return 1; } return n*fac(n-1); } fac(5); 

En utilisant Node 6.9.2 sous Linux x64 et exécutez-le en tant que node tailcall.js --harmony --harmony_tailcalls --use-strict et résultat:

 Trace at fac (/home/tailcall.js:4:11) at fac (/home/tailcall.js:7:11) at fac (/home/tailcall.js:7:11) at fac (/home/tailcall.js:7:11) at fac (/home/tailcall.js:7:11) at Object.<anonymous> (/home/tailcall.js:10:1) at Module._compile (module.js:570:32) at Object.Module._extensions..js (module.js:579:10) at Module.load (module.js:487:32) at tryModuleLoad (module.js:446:12) 

Ce qui montre clairement que calltack est rempli d'appels et la récurrence arrière n'est pas prise en charge bien que j'utilise le dernier NodeJS.

Est-ce que NodeJS / JavaScript prend en charge la récurrence de queue? Ou est-ce que je dois vraiment aller avec les générateurs et les rendements, mais le problème ici est que mes rappels vont être très asynchrones et je ne vais pas travailler avec la valeur de retour de toute façon, je dois juste m'assurer que l'appelant ne reçoit pas inutilement Les appels alors que la fonction se réfère à elle-même en retour.

Tout d'abord, si votre cas réel vous inquiète, c'est quand une fonction s'appelle à partir d'un rappel asynchrone, alors vous n'aurez probablement aucune accumulation de pile là-bas.

C'est parce que la fonction d'origine est déjà retournée et que la totalité de la pile a été déroulée avant que le rappel asynchimique ne soit appelé, alors même qu'il semble visuellement une récurrence, il n'y a pas de compilation de pile.

Voici un simple exemple de code de la réalisation de plusieurs requêtes de réseau séquentielles où une fonction se déclenche à partir d'un rappel asynchrone et il n'y a pas d'accumulation de pile.

 function getPages(baseURL, startParam, endParam, progressCallback) { function run(param) { request(baseURL + param, function(err, response, body) { if (err) return progressCallback(err); ++param; if (param < endParam) { progressCallback(null, body); // run another iteration // there is no stack buildup with this call run(param); } else { // indicate completion of all calls progressCallback(null, null); } }); } run(startParam); } 

L'invocation antérieure de run() est déjà retournée et la pile s'est déroulée complètement avant que le rappel de rappel asynchrone ne soit appelé, alors que cela ressemble à une récurrence visuelle, il n'y a pas d'accumulation de pile.


Dans le code spécifique que vous montrez, vous pouvez éviter la récurrence entièrement en réécritant en utilisant une boucle while qui fonctionnerait efficacement dans n'importe quelle version de Javascript:

 function fac(n){ var total = 1; while (n > 1) { total *= n--; } return total; } // run demo in snippet for (var i = 1; i <= 10; i++) { console.log(i, fac(i)) } 

Ce que vous avez là n'est pas un appel de queue. Un appel de queue est un appel de fonction exécuté comme l' action finale d'une autre fonction. Un appel arrière-récursif est le même, sauf que la fonction s'appelle lui-même.

Cependant, l'action finale de votre code est n*fac(n-1) , et non fac(n-1) . Ce n'est pas un appel de queue récursif parce que la pile actuelle doit toujours se rappeler n lors du calcul des appels récursifs afin de savoir quels nombres se multiplier.

Ce que vous pouvez faire, c'est de calculer cette information 1 étape avant:

 const fac = (n, result = 1) => n === 1 ? result : fac(n - 1, n * result); console.log(fac(5)); // 120 

Je ne suis pas sûr que votre fonction récursive ait un appel arrière. Peut-être que vous pouvez essayer ce qui suit;

 "use strict"; var fac = (n, r = 1) => n === 1 ? r : (r *= n, fac(n-1,r)); console.log(fac(5));