【问题标题】:How does setTimeout prevent potential stackoverflowsetTimeout 如何防止潜在的堆栈溢出
【发布时间】:2016-10-02 06:06:42
【问题描述】:

一个例子:

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        setTimeout( nextListItem, 0);
        // ^^^^^^^^ this line
    }
};

setTimeout 的使用如何防止潜在的堆栈溢出?我了解事件队列和堆栈的概念,但我很难将两者联系起来。

【问题讨论】:

  • 如果您只是从 nextListItem 中调用 nextListItem,则堆栈将随着每次调用而增长,直到您达到退出条件(列表为空)。当你使用 setTimeout 时,外部函数将完成并从堆栈中移除,然后只有内部函数调用会在堆栈上

标签: javascript dom-events stack-overflow


【解决方案1】:

设置超时不会导致堆栈溢出,因为它是异步的。它只会将回调放入事件队列,不会阻塞执行。

第一种情况:

setTimeout 只是将回调放到事件队列中,并且父函数在不忙于堆栈的情况下退出。
即使超时时间为 0 毫秒,也会在下一个事件循环中调用,因此不会阻塞执行中的代码

var nextListItem = function() {
    var item = list.pop();

    if (item) {
         setTimeout( nextListItem, 0);
    }
};

第二种情况:

父调用子函数将新条目放入堆栈,即使父未从堆栈中清除。
最终,更多的递归调用会破坏堆栈。

var nextListItem = function() {
        var item = list.pop();
    
        if (item) {        
           nextListItem();
        }
    };

【讨论】:

    【解决方案2】:

    想想setTimeout(, 0) 安排一个函数在这个函数终止后运行。 nextListItem() 不会被递归调用,而是被 JS 环境再次调用。

    如果你执行var r() = function() { r(); };,函数会调用自身并溢出堆栈。如果您执行var r() = function() { setTimeout(r, 0); };,那么r() 将被安排在r() 终止后运行,并且它将永远运行。

    这是使用setTimeout(, 0) 而不是whilefor 循环列表的原因。它允许浏览器在下次调用nextListItem 之前处理其他事件。如果列表很长,这可以避免长时间阻塞浏览器。另一方面,如果nextListItem 正在操作 DOM,它比一次性操作要慢得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-03
      • 2012-08-24
      • 2010-10-29
      • 1970-01-01
      • 2016-07-12
      • 1970-01-01
      • 2010-09-06
      • 2011-06-24
      相关资源
      最近更新 更多