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

标签 javascript performance stack-overflow

假设我正在对一组值进行某种长时间的操作。

启动这个操作的函数是startNext()

最后一行执行的是它自己,所以它是一个像这样的递归调用:

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

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

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

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

最佳答案

是的,setTimeout 将阻止堆栈增长,因为当前函数完成并且对自身的“递归”调用被放置在事件队列中。它还会运行得更慢,因为它不是直接调用而是通过队列处理。

为了演示和证明这一点,请尝试使用 Node 进行实验。

将下面的代码示例放入一个文件中,并将 simple 标志切换到底部。您会看到 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 。为了尽可能简单,我在这里没有使用 promises。

关于javascript - 使用 setTimeout 会阻止堆栈增长吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33310551/

相关文章:

C++ 加速 map 访问

java - 由于 Java 中的递归导致的 StackOverflow 错误

Java 正则表达式匹配开始/结束标记导致堆栈溢出

javascript - 从基址调用原型(prototype)函数中声明的函数 "class": JavaScript prototyping

javascript - 页面在初始页面加载时有效,但如果我刷新页面,我会收到 Zeit、Nextjs 和现在的 404 错误

javascript - 如何获取仅日期的时间戳值

php - mysql表中保存图片源的方法

javascript - 检查瞬间时间差是否小于1秒

c++ - 从 Visual Studio 启动应用程序时出现页面错误

c - 产生堆栈溢出