【问题标题】:Does calling setTimeout clear the callstack?调用 setTimeout 会清除调用堆栈吗?
【发布时间】:2011-12-24 21:48:49
【问题描述】:

在javascript中使用setTimeout方法调用函数而不是直接调用它可以避免堆栈溢出吗?我对 setTimeout 的理解是它应该启动一个新的调用堆栈。当我查看 chrome 和 IE 的调用堆栈时,似乎 setTimeout 调用正在等待函数调用返回。

这只是调试器的一个属性还是我的理解有缺陷?

编辑

虽然下面提供的答案是正确的,但我遇到的实际问题与我调用 setTimeout(aFunction(), 10) 的事实有关,因为括号,它正在立即评估 aFunction。 This question 帮我解决了。

【问题讨论】:

  • 在调用setTimeout的函数返回之前,不能调用传入setTimeout的函数。所以是的,该函数确实启动了一个新的调用堆栈。

标签: javascript settimeout callstack


【解决方案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/

    【讨论】:

    • 感谢您为我指明了正确的方向,我能够根据您的回答解决问题,但是我还有另一个问题,请参阅stackoverflow.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
    • 调用栈和回调(任务)队列是完全不相关的概念。
    猜你喜欢
    • 2011-12-24
    • 2012-06-30
    • 1970-01-01
    • 2017-07-26
    • 2012-02-02
    • 1970-01-01
    • 2018-05-15
    • 2010-12-05
    相关资源
    最近更新 更多