为什么这样的递归不会出现堆栈溢出?

2023-11-25

我不明白为什么打电话recSetTimeOut()不会导致堆栈溢出错误,而recPromise() does.

const recSetTimeOut = () => {
  console.log('in recSetTimeOut');
  setTimeout(recSetTimeOut, 0)
};
recSetTimeOut();

const recPromise = () => {
  console.log('in recPromise');
  Promise.resolve().then(recPromise);
}
recPromise();

为什么会发生这种情况?它们之间有什么区别?

您能解释一下幕后的流程吗?


编辑更多信息

运行此片段Node.js v12.1.0 and Chrome DevTools:

const recSetTimeOut = () => { setTimeout(recSetTimeOut, 0); }
recSetTimeOut();

Result Node: 没有错误。

Result Chrome: 没有错误。

const recPromise = () => { Promise.resolve().then(recPromise); }
recPromise();

Result Node:

致命错误:表大小无效分配失败 - JavaScript 堆内存不足

Result Chrome: 浏览器崩溃。


让我们依次看一下。

const recSetTimeOut = () => {
  console.log('in recSetTimeOut');
  setTimeout(recSetTimeOut, 0)
};
recSetTimeOut();

这实际上不是递归。您正在注册recSetTimeOut与调度程序。当浏览器的 UI 线程空闲时,它将从列表中拉出下一个等待函数,并调用它。调用堆栈永远不会增长;调度程序(本机代码)将始终位于非常短的调用堆栈的顶部。您可以通过发出异常并检查其调用堆栈来验证这一点。

  • 这个函数实际上并不是递归的;堆栈不会增长。
  • 每次调用后,它将控制权交还给 UI 线程,从而允许处理 UI 事件。
  • 仅当 UI 完成其操作并调用下一个计划任务时,才会发生下一次调用。
const recPromise = () => {
  console.log('in recPromise');
  Promise.resolve().then(recPromise);
}
recPromise();

这实际上是一个无限循环,拒绝将控制权交还给 UI。每次承诺兑现时,then处理程序立即被调用。完成后,会出现一个then处理程序立即被调用。完成后... UI 线程将饥饿,并且 UI 事件将永远不会被处理。与第一种情况一样,调用堆栈不会增长,因为每个回调实际上都是由循环进行的。这称为“承诺链”。如果一个 Promise 解析为一个 Promise,则会调用该新 Promise,并且这不会导致堆栈增长。什么does不过,do 是阻止 UI 线程执行任何操作。

  • 实际上是无限循环。
  • 拒绝将控制权交还给 UI。
  • 调用堆栈不会增长。
  • 下一个调用会立即调用,并且具有极大的偏见。

您可以使用以下命令确认两个堆栈跟踪实际上都是空的console.log((new Error()).stack).

这两种解决方案都不应导致堆栈溢出异常,尽管这可能取决于实现;浏览器的调度程序的功能可能与 Node 的不同。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这样的递归不会出现堆栈溢出? 的相关文章

随机推荐

  • 如何在 Selenium WebDriver 中自动清除浏览器缓存?

    如何在每次测试运行之前清除浏览器缓存 我尝试过driver manage deleteAllCookies in setUp创建驱动程序实例后的方法 它适用于 Firefox 但对于 IE 不起作用 有没有IE的解决办法 请提供给我 您可以
  • .NET 中的跨进程读写同步原语?

    是否有跨进程工作的读 写锁定机制 类似于互斥锁 但读 写而不是独占锁定 我想允许并发读取访问 但允许独占写入访问 Windows 不包含跨进程读写锁 可以使用信号量和互斥量的组合来构造互斥量 互斥量由写入者持有以进行独占访问 或者由读取者持
  • 函数指针如何工作?

    我正在问一些具体问题 如何在类中初始化它们 如何将函数作为参数传递 类中是否需要声明和定义函数指针 对于问题 2 我的意思是 void s void void f function what should I put as type to
  • 通过 jquery 上下移动选择选项

    所以我让这段代码适用于 Firefox 和 Chrome 它的作用是允许您在 HTML 选择表单中重新排序选项 但是当我通过 IE8 测试代码时 它有点不完整 它仅适用于前几次单击 之后您必须多次单击该按钮才能使其工作 有谁知道任何其他代码
  • 解析 HTML 表格最快、最简单、最好的方法是什么?

    我正在尝试获取这张桌子http www datamystic com timezone time zones html转换为数组格式 这样我就可以用它做任何我想做的事情 最好是 PHP Python 或 JavaScript 这种问题经常出
  • RxJs 可观察分页

    第一 这是我使用 RxJs 的第一个项目 我想通过使用它我会学到最好的东西 我找到了这个答案 使用 RxJs 将分页请求转换为 Observable 流但评论里却说 您仍然超出了最大调用堆栈 返回大约 430 页 我认为递归可能不是最好的解
  • Shift + 鼠标滚轮水平滚动

    对于水平滚动 使用 Shift 滚轮相当常见 这两者都相当容易捕获 我可以使用 MouseWheel 事件以及由 KeyDown KeyUp 事件设置的标志来跟踪何时按下 Shift 键 但是 如何真正触发水平滚动呢 我知道 WM MOUS
  • GHC 7.7 中引入的自由覆盖条件破坏了 GHC 7.6 中有效的代码

    The idea 我正在写一个DSL 编译为 Haskell 该语言的用户可以定义自己的不可变数据结构和关联函数 我所说的关联函数是指属于数据结构的函数 例如 用户可以编写 用 pythonic 伪代码 data Vector a x y
  • 如何使用 gitpython 对当前提交与上次提交进行 git diff?

    我正在尝试掌握 gitpython 模块 hcommit repo head commit tdiff hcommit diff HEAD 1 but tdiff hcommit diff HEAD HEAD 不起作用 也不 HEAD HE
  • 是否可以为 UITableView 添加边框样式? (不是边框颜色/边框宽度)

    是否可以为 UITableView 添加边框样式 不仅仅是边框颜色和边框宽度 例如凹槽边框样式 嘿 您可以使用以下方式为您的视图添加边框CALayer可以在QuartzCore Framework 以下链接将帮助您了解CALayer详细 C
  • 如何使用 Eclipse 将特定于平台的本机库包含在 .JAR 文件中?

    我刚刚开始学习JNI 我一直在遵循一个简单的示例 并且创建了一个调用本机库中的 Hello World 方法的 Java 应用程序 我想以 Win32 和 Linux x86 为目标 我的库驻留在 DLL 中 当 DLL 添加到 Eclip
  • 如何阻止 Eclipse 在每次捕获的异常时崩溃?

    当我开始调试 java 项目时 Eclipse 不断地破坏第 3 方库中的随机异常 这非常烦人 知道如何阻止这个吗 我尝试单击断点视图上的 图标 我可以看到 挂起捕获的异常 和 挂起未捕获的异常 复选框都没有选中 Eclipse 仍然在异常
  • SCSS 与字符串的算术运算

    selector width 10px width width 2 output 10px but expected 5px 上面的代码是不言自明的 请纠正我 您可以使用calc功能 selector width 10px width ca
  • 为什么不能在字节变量中存储负值?

    我正在转换可以在 Java 中运行但不能在 C 中运行的代码 byte buffer new byte 64 this buffer int this count 0x3F 128 这会生成编译时错误 常量值 128 无法转换为 字节 如何
  • 如何在 MySQL 中创建时间点架构

    I read this有关为数据库构建时间点架构的文章 在我看来 这是一个优雅的解决方案 但这篇文章是在不久前 2007 年 就已经准备好的 我想知道 1 还有其他方法可以解决这个问题吗 2 这种方法和其他方法的优缺点是什么 3 是否有可供
  • Burn 中的RegistrySearch 与 util:RegistrySearch

    我在用Burn构建 WiX 引导程序 我意识到如下所示的RegistrySearch实际上并没有搜索注册表 我用了过程监控器监视注册表访问
  • Haskell:类型推断和函数组合

    这个问题的灵感来自于此answer另一个问题 表明您可以使用定义为的函数从列表中删除每个出现的元素 removeall filter 用铅笔和纸根据以下类型进行计算filter and 该函数的类型为 removeall Eq a gt a
  • 将 Ruby 对象序列化为 JSON 并返回?

    我想将一个对象序列化为 JSON 将其写入文件并读回 现在我希望在 net 中有类似的东西 你有 json net 或类似的东西 然后你会这样做 JsonSerializer Serialize obj 并完成它 您将返回 JSON 字符串
  • asp.net mvc 中的动态子域

    我对 ASP NET 相当陌生 对 IIS 也没什么经验 我想让我的应用程序的每个用户都有自己的子域 但都使用相同的控制器 然后子域将控制显示的内容 Example user1subdomain mydomain com Whatever
  • 为什么这样的递归不会出现堆栈溢出?

    我不明白为什么打电话recSetTimeOut 不会导致堆栈溢出错误 而recPromise does const recSetTimeOut gt console log in recSetTimeOut setTimeout recSe