请教 setTimeout 为什么不会导致栈溢出?

2014-10-20 17:12:28 +08:00
 revir

最近在用js处理任务的时候发生了栈溢出问题, 原来是递归调用函数的次数太多, 函数栈没有来得及释放导致, 用 setTimeout 调用递归函数解决了我的问题, 但是还有一些不明之处, 请大家指教!

  1. 下面这个函数明显会导致栈溢出, 用node执行到 17000 多次的时候就报: RangeError: Maximum call stack size exceeded.
func1 = function(i) {
    i += 1;
    console.log(i);
    if (i > 1000000) {
      return;
    }
    return func1(i);
  };

  func1(0);
  1. 但是改成用 setTimeout 调用就不会。
func2 = function(i) {
    i += 1;
    console.log(i);
    if (i > 1000000) {
      return;
    }
    return setTimeout((function() {
      return func2(i);
    }), 0);
  };

  func2(0);

我的疑惑就是 setTimeout 中的函数调用时, 之前的函数堆栈有没有被释放呢? 如果有的话变量 i 是怎么传进去的; 如果没有的话, 这段代码为什么不会栈溢出。

5972 次点击
所在节点    JavaScript
8 条回复
xieguanglei
2014-10-20 17:25:38 +08:00
前一个,直到栈溢出,第一次func1函数调用都没有结束。

后一个
return setTimeout((function() {
return func2(i);
}), 0);
这一句之后,这一次func2函数就结束了。

函数堆栈释放,变量i没有释放,具体请Google下 [闭包] 。
revir
2014-10-20 17:36:41 +08:00
@xieguanglei
嗯, 闭包的概念懂一些, 但是有些糊涂, 后一次堆栈释放了, 变量i没有释放吗? 那这样应该也会造成内存泄漏吧?
谢谢!
revir
2014-10-20 17:41:03 +08:00
@xieguanglei
刚刚测试发现内存占用确实会越来越大, gotcha!
revir
2014-10-20 17:50:24 +08:00
神奇的闭包原来是创建在堆上的, 难怪不会栈溢出!
chemzqm
2014-10-20 18:04:34 +08:00
我的理解是 setTimeout 实际上把函数丢到下一次轮询的栈队列去了,i是值传递,如果是对象还有可能造成内存泄露
jakwings
2014-10-20 18:41:35 +08:00
@chemzqm 这和 for 循环操作差不多。

@revir 问题是 setTimeout 是即时返回一个 Timer ID 的啊,都没有递归。
revir
2014-10-21 09:50:57 +08:00
@chemzqm
不对, 我试过了, 如果是对象, 也是一样的。

@jakwings
嗯,确实没有递归
chemzqm
2014-10-21 10:48:59 +08:00
@revir 可能会引用无法释放导致内存泄露,不会栈溢出

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/140272

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX