其他分享
首页 > 其他分享> > 使用setTimeout会阻止堆栈增长吗?

使用setTimeout会阻止堆栈增长吗?

作者:互联网

可以说我正在为一组值进行某种长时间的操作.

启动此操作的函数是startNext()

并且其中执行的最后一行本身就是它本身,因此它是一个递归调用,如下所示:

function startNext(){
   var val = getNextValue()
   workOnValue(val)
      .then(doSomeMoreWork)
      .then(doMoreStuff)
      .then(moree)
      .then(startNext);
}

这将使堆栈增长,因为尾递归在JS中尚不起作用(尚未).
将最后一行更改为:

.then(function(){setTimeout(startNext, 0)});

工作更好?
它不会填充堆栈,因为它向事件循环添加了新操作吗?

解决方法:

是的,setTimeout将阻止堆栈增长,因为当前函数已完成,并且对其自身的“递归”调用被放置在事件队列中.由于它不会被直接调用而是通过队列进行处理,因此它的运行速度也会变慢.

为了证明这一点,请尝试使用Node进行实验.

将下面的代码示例放入文件中,然后将简单标志切换到底部.您将看到recurseSimple函数超级快地运行,并且非常快速地释放堆栈. recurseTimeout运行较慢,但将永远运行.

function recurseSimple(count) {
// Count: 15269, error: bootstrap_node.js:392
// RangeError: Maximum call stack size exceeded
  try {
    if (count % 10000 === 0) {
      console.log('Running count:', count);
    }
    recurseSimple(count + 1);
  } catch (e) {
    console.log(`Simple count: ${count}, error:`, e);
  }
}

function recurseTimeout(count) {
  // No stack exceeded
  try {
    if (count % 10000 === 0) {
      console.log('Running count:', count);
    }
    setTimeout(recurseTimeout.bind(null, count + 1), 0);
  } catch (e) {
    console.log(`Timeout count: ${count}, error:`, e);
  }
}

const simple = false;

if (simple) {
  recurseSimple(0);
} else {
  recurseTimeout(0);
}

完全相同的原则适用于承诺.我在这里没有使用Promise来使其尽可能简单.

标签:performance,stack-overflow,javascript
来源: https://codeday.me/bug/20191119/2036941.html