Last active
May 17, 2020 17:35
-
-
Save XoseLluis/e92d84a6e23adb4b868849455a61e296 to your computer and use it in GitHub Desktop.
Avoiding to overfill the stack thanks to setTimeout
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 largeValue = 50000; | |
function factorial(n) { | |
return (n !== 0) ? n * factorial(n - 1) : 1; | |
} | |
function tailFactorial(n, total = 1) { | |
//console.trace(); | |
if (n === 0) { | |
return total; | |
} // proper tail call | |
return tailFactorial(n - 1, n * total); | |
} | |
function runFactorial(factorialFn){ | |
let res = factorialFn(smallValue); | |
console.log("factorial: " + res); | |
//stack overflow: | |
try { | |
res = factorialFn(largeValue); | |
console.log("factorial: " + res); | |
} | |
catch (ex){ | |
console.log("Exception: " + ex.message); | |
//Maximum call stack size exceeded | |
} | |
} | |
[factorial, tailFactorial].forEach(fn => runFactorial(fn)); | |
//for both functions we get a max stack exception, so node no longer implements TCO | |
//-------------------------- | |
function factorialWithTimeoutAndPromise(n, total = 1, resFn){ | |
//initial call | |
if (resFn === undefined){ | |
var pr = new Promise((res, rej) => resFn = res); | |
} | |
if (n === 0){ | |
resFn(total); | |
} | |
else{ | |
setTimeout(() => factorialWithTimeoutAndPromise(n-1, n * total, resFn), 0); | |
} | |
return pr; | |
} | |
function factorialWithTimeoutAndPromiseImproved(n, total = 1, counter = 0, resFn){ | |
//initial call | |
if (resFn === undefined){ | |
var pr = new Promise((res, rej) => resFn = res); | |
} | |
if (n === 0){ | |
resFn(total); | |
} | |
else{ | |
if (counter < 50){ | |
factorialWithTimeoutAndPromiseImproved(n-1, n * total, ++counter, resFn); | |
} | |
else{ | |
setTimeout(() => factorialWithTimeoutAndPromiseImproved(n-1, n * total, 0, resFn), 0); | |
} | |
} | |
return pr; | |
} | |
(async() => { | |
console.log("-- using Timeout"); | |
let options = [ | |
{ | |
name: "Not optimized", | |
value: smallValue, | |
fn: factorialWithTimeoutAndPromise | |
}, | |
{ | |
name: "Not optimized", | |
value: largeValue, | |
fn: factorialWithTimeoutAndPromise | |
}, | |
{ | |
name: "Optimized", | |
value: smallValue, | |
fn: factorialWithTimeoutAndPromiseImproved | |
}, | |
{ | |
name: "Optimized", | |
value: largeValue, | |
fn: factorialWithTimeoutAndPromiseImproved | |
} | |
]; | |
for (let option of options) { | |
console.log(option.name + " factorial for: " + option.value); | |
let start = Date.now(); | |
let result = await option.fn(option.value); | |
console.log("result: " + result); | |
console.log("it took " + (Date.now() - start) + "\n"); | |
} | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment