Skip to content

Instantly share code, notes, and snippets.

@XoseLluis
Last active October 3, 2021 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save XoseLluis/fdfcd607c2dfcc560a1a67e992535e42 to your computer and use it in GitHub Desktop.
Save XoseLluis/fdfcd607c2dfcc560a1a67e992535e42 to your computer and use it in GitHub Desktop.
Usina await to avoid stack overflows in recursive functions (works both for tail and not tail recursion)
const smallValue = 100;
const mediumValue = 1000;
const largeValue = 50000;
function factorial(n) {
//return (n !== 0) ? n * factorial(n - 1) : 1;
if(n === 0 || n === 1){
return 1;
}
else{
return n * factorial(n-1);
}
}
function tailFactorial(n, total = 1) {
if (n === 0) {
return total;
}
else{
// proper tail call
return tailFactorial(n - 1, n * total);
}
}
function runFactorial(factorialFn){
[smallValue, mediumValue, largeValue].forEach((value) => {
let res;
try {
res = factorialFn(value);
}
catch (ex){
res = "Maximum call stack size exceeded";
}
console.log(`factorial ${value}: ${res}`);
});
console.log("-------------------");
}
console.log("- NOT using await");
[factorial, tailFactorial].forEach(fn => runFactorial(fn));
//for both factorial functions (tail and not tail) we get a max stack exception, so node no longer implements TCO
//--------------------------
console.log("- using await");
async function wrongTailFactorialWithAwait(n, total = 1, resFn){
if (n === 0){
return total;
}
else{
//this first await prevents the stack overflow as we are exiting the function before the recursive call!
//await "nothing";
//if we comment the await above, we'll get the stack overflows, cause this second await here is AFTER the recursive call, so we continue to stack them up!
return await wrongTailFactorialWithAwait(n-1, n * total);
}
}
async function tailFactorialWithAwait(n, total = 1, resFn){
if (n === 0){
return total;
}
else{
//this await prevents the stack overflow as we are exiting the function before the recursive call!
await "nothing";
//if we comment the await above, we'll get the stack overflows, cause this second await here is AFTER the recursive call, so we continue to stack them up!
return await tailFactorialWithAwait(n-1, n * total);
}
}
async function factorialWithAwait(n) {
if(n === 0 || n === 1){
return 1;
}
else{
await "nothing";
return (n * await factorialWithAwait(n-1));
}
}
//async main
(async () => {
for (let asyncFn of [wrongTailFactorialWithAwait, tailFactorialWithAwait, factorialWithAwait]){
for (let value of [smallValue, mediumValue, largeValue]){
let result;
try{
result = await asyncFn(value);
}
catch(ex){
result = "Maximum call stack size exceeded";
}
console.log(`${asyncFn.name} for ${value}: ${result}`);
}
}
})();
//setTimeout(()=>console.log("done"), 5000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment