调用 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 时超出了最大调用堆栈大小

js 函数内部创建的setTimeout调用自身函数

Android:清除活动堆栈

UIKit pushViewController:animated: 上的调用会导致最终的堆栈溢出(或其他异常)吗?

每个线程都有自己的堆栈吗?

没有setTimeout 0,不理解堆栈