调用 setTimeout 会清除调用堆栈吗?
Posted
技术标签:
【中文标题】调用 setTimeout 会清除调用堆栈吗?【英文标题】:Does calling setTimeout clear the callstack? 【发布时间】:2011-12-24 21:48:49 【问题描述】:javascript中是否可以通过使用setTimeout方法调用函数而不是直接调用来避免堆栈溢出?我对 setTimeout 的理解是它应该启动一个新的调用堆栈。当我查看 chrome 和 IE 的调用堆栈时,似乎 setTimeout 调用正在等待函数调用返回。
这只是调试器的一个属性还是我的理解有缺陷?
编辑
虽然下面提供的答案是正确的,但我遇到的实际问题与我调用 setTimeout(aFunction(), 10) 的事实有关,因为括号,它正在立即评估 aFunction。 This question 帮我解决了。
【问题讨论】:
在调用setTimeout
的函数返回之前,不能调用传入setTimeout
的函数。所以是的,该函数确实启动了一个新的调用堆栈。
【参考方案1】:
还有另一种方法可以在不使用 setTimeout() 的情况下清除调用堆栈:
testLoop.js
let counter = 0;
const max = 1000000000n // 'n' signifies BigInteger
Error.stackTraceLimit = 100;
const A = () =>
fp = B;
const B = () =>
fp = A;
let fp = B;
const then = process.hrtime.bigint();
loop: // label is not needed for this simple case though useful for some cases
for(;;)
counter++;
if (counter > max)
const now = process.hrtime.bigint();
const nanos = now - then;
console.log( "runtime(sec)": Number(nanos) / (1000000000.0) )
throw Error('exit')
fp()
continue loop;
输出:
$ node testLoop.js
'runtime(sec)': 20.647399601
C:\Users\jlowe\Documents\Projects\clearStack\testLoop.js:25
throw Error('exit')
^
Error: exit
at Object.<anonymous> (C:\Users\jlowe\Documents\Projects\clearStack\testLoop.js:25:11)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
【讨论】:
【参考方案2】:我可以确认堆栈已被清除。
考虑这种情况:
function a()
b();
function b()
c();
function c()
debugger;
setTimeout( d, 1000 );
function d()
debugger;
a();
所以有两个断点——一个在函数c
的开头,一个在函数d
的开头。
在第一个断点处堆栈:
c() b() a()堆栈在第二个断点:
d()现场演示: http://jsfiddle.net/nbf4n/1/
【讨论】:
感谢您为我指出正确的方向,我能够根据您的回答解决,但是我还有另一个问题请参阅***.com/questions/8058996/… 如果这个人是对的,你的答案怎么可能是正确的答案?因为这家伙说它总是首先将你的回调推送到回调队列,而不是堆栈,所以如果调用堆栈中仍在运行同步命令,或者堆栈中的其他东西它必须等待超过指定的毫秒数.所以它并不总是 1:1。这可能会发生:嘿,我给了你 5 秒的延迟,你为什么要在 7 秒后回来?因为回调必须再等一会儿。 youtu.be/8aGhZQkoFbQ?t=782 您通过setTimeout
的第二个参数指定的延迟并不能保证回调将在该特定时间点被调用,true;但我的回答并没有说会。
我认为重要的是要注意d()
将在单独的调用堆栈中以c()
的范围调用。因此,尽管c()
在执行d()
时已经完成到a()
,但它们的局部变量仍然保留在内存中,直到d()
完成并且GC 开始清理空间。【参考方案3】:
异步调用,例如来自setTimeout
的调用,确实会生成一个新的调用堆栈。
当您说“当我查看 chrome 和 IE 的调用堆栈时,似乎 setTimeout 调用正在等待函数调用返回”时,您所描述的内容并不完全清楚。但是,您可以做的一件事是在setTimeout
调用的函数中放置一个断点,然后查看调用堆栈是否为空。
【讨论】:
你知道为什么我仍然可以在调试器中看到整个调用堆栈吗?是因为我从 setTimeout 调用的函数使用闭包来获取某些局部变量吗? @AranMulholland 你在哪里调用调试器?在传递给setTimeout
的函数内部?
@ŠimeVidas 根据问题,我只是查看浏览器调试器(Chrome 和 IE)中的调用堆栈
如果你说他们生成了一个新的调用堆栈,那么这个人说它可能还必须在回调队列中等待是不是错了? youtu.be/8aGhZQkoFbQ?t=782
调用栈和回调(任务)队列是完全不相关的概念。以上是关于调用 setTimeout 会清除调用堆栈吗?的主要内容,如果未能解决你的问题,请参考以下文章
错误 RangeError:使用 setTimeout 时超出了最大调用堆栈大小