Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive setTimeout makes stack grow

As far as I understood from these two answers:

  • Will a recursive 'setTimeout' function call eventually kill the JS Engine
  • Does use of recursive process.nexttick let other processes or threads work

when setTimeout is called recursively the stack should not grow but if you open a devtool in either FF or Chrome and run this function:

function recur(n = 10) {
  console.trace();
  console.log(n, '++++++++++++++++');
  if (n > 0) setTimeout(() => recur(--n), 1000);
}

recur()

you'll see the following:

console.trace() debugger eval code:2:11
    recur debugger eval code:2
    <anonymous> debugger eval code:8
10 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
9 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
8 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
7 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
6 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
5 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
4 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
3 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
2 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
1 ++++++++++++++++ debugger eval code:3:11
console.trace() debugger eval code:2:11
    recur debugger eval code:2
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    recur debugger eval code:4
    (Async: setTimeout handler)
    recur debugger eval code:4
    <anonymous> debugger eval code:8
0 ++++++++++++++++

Why is that?

like image 849
Sam R. Avatar asked Jul 20 '26 13:07

Sam R.


1 Answers

Asynchronous stack traces in Chrome Developer Tools... designed to keep you sane whilst trying to figure out why your function is in a setTimeout() recursion loop

  • https://www.html5rocks.com/en/tutorials/developertools/async-call-stack/

The javascript thread model is the event loop. - https://flaviocopes.com/javascript-event-loop/

setTimeout() isn't technically recursing. Its an asynchronous function call. Inside the thread, it returns a numeric id synchronously (you can call clearTimeout() on it), then as a side effect adds your function to the end of the event-loop stack. Its a singlethreaded model, so once javascript has finished your current thread/event, it will pop the next function off the top of the stack for execution. Thus 1000ms is simply the minimum time for scheduled execution but it might take longer if it is still executing previous events/threads.

If I run this code in node, I get:

node
Welcome to Node.js v12.5.0.
Type ".help" for more information.
> function recur(n = 10) {
...   console.trace();
...   console.log(n, '++++++++++++++++');
...   if (n > 0) setTimeout(() => recur(--n), 1000);
... }
undefined
> 
> recur()
Trace
    at recur (repl:2:11)
    at repl:1:1
    at Script.runInThisContext (vm.js:123:20)
    at REPLServer.defaultEval (repl.js:384:29)
    at bound (domain.js:415:14)
    at REPLServer.runBound [as eval] (domain.js:428:12)
    at REPLServer.onLine (repl.js:700:10)
    at REPLServer.emit (events.js:205:15)
    at REPLServer.EventEmitter.emit (domain.js:471:20)
    at REPLServer.Interface._onLine (readline.js:314:10)
10 ++++++++++++++++
undefined
> Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
9 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
8 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
7 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
6 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
5 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
4 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
3 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
2 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
1 ++++++++++++++++
Trace
    at recur (repl:2:11)
    at Timeout._onTimeout (repl:4:31)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
0 ++++++++++++++++
like image 58
James McGuigan Avatar answered Jul 23 '26 03:07

James McGuigan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!