递归 setTimeout 使堆栈增长

Recursive setTimeout makes stack grow

据我从这两个答案中了解到:

当递归调用 setTimeout 时,堆栈不应增长,但如果您在 FF 或 Chrome 和 运行 中打开开发工具,则此函数:

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

recur()

您将看到以下内容:

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 ++++++++++++++++

这是为什么?

Chrome 开发人员工具中的异步堆栈跟踪...旨在让您在尝试弄清楚为什么您的函数处于 setTimeout() 递归循环中时保持理智

javascript线程模型就是事件循环。 - https://flaviocopes.com/javascript-event-loop/

setTimeout() 在技术上不是递归的。它是一个异步函数调用。在线程内部,它同步 returns 一个数字 ID(您可以对其调用 clearTimeout()),然后作为副作用将您的函数添加到事件循环堆栈的末尾。它是一个单线程模型,所以一旦 javascript 完成了当前的 thread/event,它将从堆栈顶部弹出下一个函数以供执行。因此 1000ms 只是计划执行的最短时间,但如果仍在执行之前的 events/threads.

,则可能需要更长的时间

如果我在节点中 运行 这段代码,我得到:

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 ++++++++++++++++