JavaScript:为什么递归不会停止?

如果我foo++用 a换行,为什么递归不会停止setTimeout?我很确定我错过了有关异步操作的主要 JavaScript 概念。

let foo = 0;

const bar = () => {
  setTimeout(() => foo++);

  if (foo <= 2) {
    bar();
  }
}

bar();

回答

当您调用bar()它时,它会被添加到称为调用堆栈的东西中。调用堆栈用于在我们调用函数和从函数返回时跟踪我们在脚本中的位置。当一个函数被调用时,它被添加到调用堆栈中,当它返回时它从调用堆栈中弹出。

Stack:
- bar()

bar()运行时,它调用setTimeout()其被添加到调用堆栈。

setTimeout()函数启动 Web API 并完成/返回,将其从调用堆栈中弹出。然后 Web API 等待 0 毫秒(0 毫秒,因为当没有延迟传递给 setTimeout 时,它默认为 0)并将您的() => foo++回调推送/排队到称为任务队列的东西上。

Task queue: (front ---- back)
() => foo++

仅当调用堆栈为空时,事件循环才会将任务队列中的任务从队列中弹出/出队。这很重要,因为这意味着上述递增的回调foo只会在bar()返回后被调用(从而将其从调用堆栈中弹出),但是,这永远不会发生,bar()因为您的 if 条件将始终为真,继续不断地调用自身,因此,将继续添加bar()到调用堆栈中。

Stack:
- bar() // after first recursive call
- bar()

当您继续调用bar()递归函数时,您的调用堆栈开始填满,您的任务队列也是如此:

Stack:
- bar() // after N recursive calls
...
- bar()
- bar()

由于您的调用堆栈永远没有机会从堆栈中弹出 bar(),它会继续增长,从而出现“超出最大调用堆栈大小”错误。


以上是JavaScript:为什么递归不会停止?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>