JavaScript: 为什么递归不会停止?
JavaScript: Why the recursion won't stop?
如果我将 foo++
包装在 setTimeout
中,为什么递归不会停止?我很确定我错过了关于 异步 操作的主要 JavaScript 概念。
let foo = 0;
const bar = () => {
setTimeout(() => foo++);
if (foo <= 2) {
bar();
}
}
bar();
当您调用 bar()
时,它会添加到名为 call stack 的对象中。调用堆栈用于跟踪我们在调用函数时在脚本中的位置和 return。当一个函数被调用时,它会被添加到调用堆栈中,当它 return 时,它会从调用堆栈中弹出。
Stack:
- bar()
当 bar()
运行时,它调用 setTimeout()
并添加到调用堆栈中。
setTimeout()
函数启动 Web API 和 finishes/returns,将其从调用堆栈弹出。 Web API 然后等待 0 毫秒(0 毫秒,因为当没有延迟传递给 setTimeout 它默认为 0)并且 pushes/enqueues 你的 () => foo++
回调到称为任务队列的东西。
Task queue: (front ---- back)
() => foo++
仅当调用堆栈为空时,任务队列中的任务才会 popped/dequeued 通过 事件循环 从队列中移出。这很重要,因为这意味着上面增加 foo
的回调只会在 bar()
被 returned 时调用(因此将其从调用堆栈中弹出),但是,这永远不会发生,因为 bar()
继续不断地调用自己,因为您的 if 条件将始终为真,因此,将继续将 bar()
添加到调用堆栈中。
Stack:
- bar() // after first recursive call
- bar()
当您继续在递归函数中调用 bar()
时,您的调用堆栈开始填满,您的任务队列也是如此:
Stack:
- bar() // after N recursive calls
...
- bar()
- bar()
由于您的调用堆栈从来没有机会从堆栈中弹出 bar(),它会继续增长,给您一个“超出最大调用堆栈大小”的错误。
您应该将递归放在 setTimeout 回调中:
let foo = 0;
const bar = () => {
foo++;
if (foo <= 2) setTimeout(bar);
};
bar();
console.log(foo); // Displays 3
停止使用全局状态和副作用,您的问题就会消失 -
const delay =
500
function bar (foo = 0)
{ if (foo > 2)
return
else
setTimeout(_ => bar(foo + 1), delay)
console.log(foo)
}
bar()
如果我将 foo++
包装在 setTimeout
中,为什么递归不会停止?我很确定我错过了关于 异步 操作的主要 JavaScript 概念。
let foo = 0;
const bar = () => {
setTimeout(() => foo++);
if (foo <= 2) {
bar();
}
}
bar();
当您调用 bar()
时,它会添加到名为 call stack 的对象中。调用堆栈用于跟踪我们在调用函数时在脚本中的位置和 return。当一个函数被调用时,它会被添加到调用堆栈中,当它 return 时,它会从调用堆栈中弹出。
Stack:
- bar()
当 bar()
运行时,它调用 setTimeout()
并添加到调用堆栈中。
setTimeout()
函数启动 Web API 和 finishes/returns,将其从调用堆栈弹出。 Web API 然后等待 0 毫秒(0 毫秒,因为当没有延迟传递给 setTimeout 它默认为 0)并且 pushes/enqueues 你的 () => foo++
回调到称为任务队列的东西。
Task queue: (front ---- back)
() => foo++
仅当调用堆栈为空时,任务队列中的任务才会 popped/dequeued 通过 事件循环 从队列中移出。这很重要,因为这意味着上面增加 foo
的回调只会在 bar()
被 returned 时调用(因此将其从调用堆栈中弹出),但是,这永远不会发生,因为 bar()
继续不断地调用自己,因为您的 if 条件将始终为真,因此,将继续将 bar()
添加到调用堆栈中。
Stack:
- bar() // after first recursive call
- bar()
当您继续在递归函数中调用 bar()
时,您的调用堆栈开始填满,您的任务队列也是如此:
Stack:
- bar() // after N recursive calls
...
- bar()
- bar()
由于您的调用堆栈从来没有机会从堆栈中弹出 bar(),它会继续增长,给您一个“超出最大调用堆栈大小”的错误。
您应该将递归放在 setTimeout 回调中:
let foo = 0;
const bar = () => {
foo++;
if (foo <= 2) setTimeout(bar);
};
bar();
console.log(foo); // Displays 3
停止使用全局状态和副作用,您的问题就会消失 -
const delay =
500
function bar (foo = 0)
{ if (foo > 2)
return
else
setTimeout(_ => bar(foo + 1), delay)
console.log(foo)
}
bar()