JavaScript:为什么递归获胜';停不下来?
如果我将JavaScript:为什么递归获胜';停不下来?,javascript,asynchronous,recursion,callback,settimeout,Javascript,Asynchronous,Recursion,Callback,Settimeout,如果我将foo++包装在setTimeout中,为什么递归不会停止?我很确定我错过了一个关于异步操作的主要JavaScript概念 设foo=0; 常数条=()=>{ setTimeout(()=>foo++); 如果(foo当您调用bar()时,它会被添加到名为的内容中。调用堆栈用于跟踪我们在脚本中调用函数和从函数返回时的位置。当调用函数时,它会被添加到调用堆栈中,当它返回时,它会从调用堆栈中弹出 Stack: - bar() Stack: - bar() // after first r
foo++
包装在setTimeout
中,为什么递归不会停止?我很确定我错过了一个关于异步操作的主要JavaScript概念
设foo=0;
常数条=()=>{
setTimeout(()=>foo++);
如果(foo当您调用bar()
时,它会被添加到名为的内容中。调用堆栈用于跟踪我们在脚本中调用函数和从函数返回时的位置。当调用函数时,它会被添加到调用堆栈中,当它返回时,它会从调用堆栈中弹出
Stack:
- bar()
Stack:
- bar() // after first recursive call
- bar()
当运行bar()
时,它调用setTimeout()
,该调用被添加到调用堆栈中
Stack:
- bar()
Stack:
- bar() // after first recursive call
- bar()
setTimeout()
函数启动Web API并完成/返回,将其从调用堆栈中弹出。然后,Web API等待0毫秒(0毫秒,当没有延迟传递给setTimeout时,它默认为0),并将您的()=>foo++
回调推送到任务队列上
Task queue: (front ---- back)
() => foo++
只有在调用堆栈为空时,事件循环才会将任务队列中的任务弹出/退出队列。这一点很重要,因为这意味着只有在返回bar()
后才会调用上述以foo
为增量的回调(从而将其弹出调用堆栈),但是,这不会发生,因为bar()
继续不断地调用自身,因为if条件始终为true,因此将继续向调用堆栈添加bar()
Stack:
- bar()
Stack:
- bar() // after first recursive call
- bar()
在递归函数中继续调用bar()
时,调用堆栈开始填满,任务队列也开始填满:
Stack:
- bar() // after N recursive calls
...
- bar()
- bar()
由于您的调用堆栈从来没有机会将bar()弹出堆栈,因此它会继续增长,从而出现“超出最大调用堆栈大小”错误。当您调用bar()时
它被添加到名为的内容中。调用堆栈用于在调用函数和从函数返回时跟踪脚本中的位置。当调用函数时,它被添加到调用堆栈中,当它返回时,它从调用堆栈中弹出
Stack:
- bar()
Stack:
- bar() // after first recursive call
- bar()
当运行bar()
时,它调用setTimeout()
,该调用被添加到调用堆栈中
Stack:
- bar()
Stack:
- bar() // after first recursive call
- bar()
setTimeout()
函数启动Web API并完成/返回,将其从调用堆栈中弹出。然后,Web API等待0毫秒(0毫秒,当没有延迟传递给setTimeout时,它默认为0),并将您的()=>foo++
回调推送到任务队列上
Task queue: (front ---- back)
() => foo++
只有在调用堆栈为空时,事件循环才会将任务队列中的任务弹出/退出队列。这一点很重要,因为这意味着只有在返回bar()
后才会调用上述以foo
为增量的回调(从而将其弹出调用堆栈),但是,这不会发生,因为bar()
继续不断地调用自身,因为if条件始终为true,因此将继续向调用堆栈添加bar()
Stack:
- bar()
Stack:
- bar() // after first recursive call
- bar()
在递归函数中继续调用bar()
时,调用堆栈开始填满,任务队列也开始填满:
Stack:
- bar() // after N recursive calls
...
- bar()
- bar()
由于调用堆栈从来没有机会将bar()弹出堆栈,因此它会继续增长,从而出现“超出最大调用堆栈大小”错误。您应该将递归放在setTimeout回调中:
设foo=0;
常数条=()=>{
foo++;
如果(foo您应该将递归放在setTimeout回调中:
设foo=0;
常数条=()=>{
foo++;
如果(foo停止使用全局状态和副作用,你的问题就会消失-
常数延迟=
500
功能栏(foo=0)
{如果(foo>2)
回来
其他的
setTimeout(=>bar(foo+1),延迟)
console.log(foo)
}
bar()
停止使用全局状态和副作用,问题就会消失-
常数延迟=
500
功能栏(foo=0)
{如果(foo>2)
回来
其他的
setTimeout(=>bar(foo+1),延迟)
console.log(foo)
}
bar()
超时回调将在当前函数完成后的某个时间执行。但它永远不会完成。@deceze它在其中指的是什么永远不会完成?另外,众所周知,超时回调总是在某个时间后执行,我正在寻找一个深入的解释:)这是你的函数,bar
。只需清空setTimeout
的内容,无论出于何种目的,传递给setTimeout
的内容都是无关的。阅读bar
时忽略它,很明显这只是一个无休止的递归,因此它永远不会结束。@deceze抱歉地说,但解释是错误的非常模糊,建议只忽略一行代码。好吧,同样,出于所有目的和目的,您可以忽略这行代码,它不会产生任何影响。超时回调将在当前函数完成后的某个时间执行。但它永远不会完成。@deceze它在“永远不会完成”中指的是什么?另外,它是一个kn我自己的一个事实是,超时回调总是在一段时间后执行,我一直在寻找一个深入的解释:)这是你的函数,bar
。只需清空setTimeout
的内容,无论出于何种目的,传递给setTimeout
的内容都是无关的。阅读bar
时忽略它,很明显这只是一个无休止的递归,因此它永远不会结束。@deceze抱歉地说,但解释是错误的非常模糊,建议只忽略一行代码。好吧,同样,出于所有意图和目的,您可以忽略这行代码,它不会产生任何影响。请不要盲目发布解决方案,即使没有一次试运行。它不会记录3,而是记录1,因为setTimeout
是异步的,并且在执行之前会记录foo。如果您简单地将在调用bar()
之前增加,即