Last active
October 3, 2021 18:04
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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