在 JavaScript 中递归构建 Promise 链 - 内存注意事项

2024-03-22

In 这个答案 https://stackoverflow.com/a/29906627/3478010,递归地构建承诺链。

稍微简化一下,我们有:

function foo() {
    function doo() {
        // always return a promise
        if (/* more to do */) {
            return doSomethingAsync().then(doo);
        } else {
            return Promise.resolve();
        }
    }
    return doo(); // returns a promise
}

想必这会产生一个调用堆栈and承诺链——即“深”和“宽”。

我预计内存峰值会比执行递归或单独构建承诺链更大。

  • 是这样吗?
  • 有没有人考虑过以这种方式构建链的内存问题?
  • Promise 库之间的内存消耗会有所不同吗?

调用堆栈和承诺链 - 即“深”和“宽”。

事实上,没有。正如我们所知,这里没有承诺链doSomeThingAsynchronous.then(doSomethingAsynchronous).then(doSomethingAsynchronous).…(这是什么Promise.each or Promise.reduce如果以这种方式编写,可能会顺序执行处理程序)。

What we are facing here is a resolve chain1 - what happens in the end, when the base case of the recursion is met, is something like Promise.resolve(Promise.resolve(Promise.resolve(…))). This is only "deep", not "wide", if you want to call it that.

我预计内存峰值会比执行递归或单独构建承诺链更大。

实际上不是尖峰。随着时间的推移,你会慢慢地建立起大量的承诺,并用最内在的承诺来解决,所有这些都代表相同的结果。当任务结束时,条件得到满足并且最里面的承诺以实际值解决,所有这些承诺都应该以相同的值解决。那最终会是O(n)沿着解析链向上走的成本(如果简单地实现,这甚至可能会递归地完成并导致堆栈溢出)。之后,除了最外面的承诺之外的所有承诺都可以被垃圾收集。

相比之下,由类似的东西构建的承诺链

[…].reduce(function(prev, val) {
    // successive execution of fn for all vals in array
    return prev.then(() => fn(val));
}, Promise.resolve())

会显示一个尖峰,分配n同时处理 Promise 对象,然后慢慢地逐一解决它们,对之前的对象进行垃圾收集,直到只有已解决的最终 Promise 还活着。

memory
  ^     resolve      promise "then"    (tail)
  |      chain          chain         recursion
  |        /|           |\
  |       / |           | \
  |      /  |           |  \
  |  ___/   |___     ___|   \___     ___________
  |
  +----------------------------------------------> time

是这样吗?

Not necessarily. As said above, all the promises in that bulk are in the end resolved with the same value2, so all we would need is to store the outermost and the innermost promise at one time. All intermediate promises may become garbage-collected as soon as possible, and we want to run this recursion in constant space and time.

事实上,这种递归结构对于异步循环 https://stackoverflow.com/a/24660323/1048572在动态条件下(没有固定的步骤数),你无法真正避免它。在 Haskell 中,它一直被用于IOmonad,正是因为这种情况才对其进行了优化。它非常类似于尾调用递归 https://en.wikipedia.org/wiki/Tail_call,它通常会被编译器消除。

有没有人考虑过以这种方式构建链的内存问题?

是的。这是在 Promise/aplus 讨论过 https://github.com/promises-aplus/promises-spec/issues/179例如,虽然还没有结果。

许多 Promise 库确实支持迭代助手以避免 Promise 峰值then链条,如蓝鸟的each and map方法。

My own promise library3,4 does feature resolve chains without introducing memory or runtime overhead. When one promise adopts another (even if still pending), they become indistinguishable, and intermediate promises are no longer referenced anywhere.

Promise 库之间的内存消耗会有所不同吗?

是的。虽然这种情况可以优化,但这种情况很少发生。具体来说,ES6 规范确实要求 Promises 在每次检查时检查该值resolve调用,因此不可能崩溃链。链中的承诺甚至可以用不同的值来解决(通过构建一个滥用吸气剂的示例对象,而不是在现实生活中)。问题在 esdiscuss 上提出 https://mail.mozilla.org/pipermail/es-discuss/2015-April/042517.html但仍未解决。

因此,如果您使用泄漏实现,但需要异步递归,那么您最好切换回回调并使用延迟反模式 https://stackoverflow.com/q/23803743/1048572将最里面的承诺结果传播到单个结果承诺。

[1]: no official terminology
[2]: well, they are resolved with each other. But we want to resolve them with the same value, we expect that
[3]: undocumented playground, passes aplus. Read the code at your own peril: https://github.com/bergus/F-Promise https://github.com/bergus/F-Promise
[4]: also implemented for Creed in this pull request https://github.com/briancavalier/creed/pull/9

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

在 JavaScript 中递归构建 Promise 链 - 内存注意事项 的相关文章

  • onclick 函数上的 CSS 选择器

    有没有办法让CSS选择器onclick function 您可以在onclick https stackoverflow com questions 24365416 select element which have specific a
  • 将 Laravel 集合/数组转换为 Javascript 数组

    我想将 Laravel 中的数组分配给 JavaScript 数组 我已经从我的AppServiceProvider和 json decoded 它像 View composer function view users Users all
  • 在堆栈已满并给出分段错误之前,C/C++ 中的最大递归函数调用次数?

    我正在做一个问题 我使用递归函数来创建线段树 对于较大的值 它开始出现分段错误 所以我之前认为可能是因为数组索引值越界 但后来我认为这可能是因为程序堆栈太大 我编写这段代码是为了计算系统出现段错误之前允许的最大递归调用次数 include
  • jQuery - 提高处理 XML 时的选择器性能

    我正在处理一个 XML 文件 当使用 XPath 样式选择器选择节点时 该文件的性能非常慢 这是运行特别慢的部分代码 for i 0 i
  • 将组件注册到现有的 Vue.js 实例

    我是 Vue js 新手 我想注册一个本地组件 如下所述 https v2 vuejs org v2 guide components html Local Registration https v2 vuejs org v2 guide
  • Webpack 和 Angular HTML 图像加载

    我一直对 webpack 和 Angular 感到头疼 这可能有一个简单的答案 但我无法弄清楚 我已经阅读了堆栈溢出中关于这个主题的几乎所有答案 但都无济于事 我有一个像这样的 html 页面 还有其他包含图像的模板 img
  • 如何在下拉列表中选择一个选项

    我正在使用 AngularJS 指令 我需要在模板中设置下拉列表的选定选项
  • 禁用整个站点的 IE8 加速器

    是的 我知道有类似的问题 https stackoverflow com questions 499565 is it possible to disable ie8 accelerators on my website在 SO 上 但它已
  • FormData 中的 Blob 为 null

    我正在尝试通过远程 API 通过 ajax 在 android 中发送创建的照片 我在用着相机图片背景 https github com an rahulpandey cordova plugin camerapicturebackgrou
  • 添加选项以选择框而不用 Internet Explorer 关闭该框?

    我正在尝试构建一个包含多个下拉选择框的网页 这些下拉选择框在首次打开时异步加载其选项 这在 Firefox 下工作得很好 但在 Internet Explorer 下则不然 下面是我想要实现的目标的一个小例子 基本上 有一个选择框 ID 为
  • 表单序列化javascript(无框架)

    想知道 javascript 中是否有一个没有 jquery 或任何框架的函数可以让我序列化表单并访问序列化版本 2023 年更新 Use FormData https developer mozilla org en US docs We
  • selenium-webdriver 与 webdriverjs 有什么区别(以及何时使用)?

    我是一位使用 selenium webdriver 的经验丰富的专业人士 我正在探索有关如何测试 javascript 应用程序的更多选项 我发现了 webdriverJs 不幸的是 我不明白这两者 2 之间有什么区别 有人可以解释一下何时
  • 不可见的 reCAPTCHA - 缺少必需的参数:sitekey

    我正在为每个带有具有类的按钮的表单动态加载不可见的 reCAPTCHAg recaptcha 我遇到的问题是验证码未正确加载 我不知道为什么 我按照验证码网站上的文档进行操作 但我不确定如何以及为什么会出现此错误 Uncaught Erro
  • 从未使用 mimeType 初始化的 MediaRecorder 获取 mimeType

    我正在使用 MediaRecorder API 在页面上录制一些媒体 在我的 MediaRecorder 初始化中 我没有指定内容类型 因为我不需要任何特别的内容 浏览器可以选择它想要的 var mediaRecorder new Medi
  • 当选项卡重新加载(chrome 扩展)时,如何运行此脚本?

    所以我想在指定 URL 中重新加载选项卡时运行脚本 它几乎可以工作 但实际上 id 不能 这是我的清单文件 manifest version 2 name Sample Extension description Sample Chrome
  • 如何仅突出显示嵌套表的最里面的表行?

    我有几个嵌套表 我想突出显示鼠标指针下方的最里面的行 我怎样才能做到这一点 一些提示 我使用嵌套表来显示递归表格数据 表可以嵌套 10 层 嵌套正如您所期望的那样 table tr td table tr td table tr td 可能
  • window.open 使用 css 样式

    我想设计我的 window open 目前 我的网页上有一些项目由于解析了某个类而打开 然后在新窗口中打开指定的文本 我想更改字体大小 字体和填充等 这是我的 JavaScript 代码
  • 在 Firefox 中使用 Javascript 检测键盘布局

    有没有办法在 Firefox 中检测客户端的键盘布局 我知道 Chrome 的答案是肯定的 请参阅https developer mozilla org en US docs Web API Navigator keyboard https
  • 角度 ng-repeat 根据条件添加样式

    我在 div 列表上使用 ng repeat 并且在渲染此 div 的 json 中手动添加项目 我需要定位我在 json 中添加的最后一个 div 它会自动在屏幕上渲染 即 couse 光标所在的位置 其余部分保持在相同位置 但没有给出渲
  • R闪亮:使用闪亮的JS从数据表中获取信息

    我想读出所有列名称以及它们在数据表中显示的顺序 由于不同的原因 我无法使用 stateSave 等选项 我对 JS 没有什么把握 但我确信用它可以完成 所以我需要你帮助我 我尝试过类似的代码片段 datatable data callbac

随机推荐

  • 如何隐藏 VsCode 滚动条?

    VsCode 中似乎曾经有一个隐藏滚动条的设置 editor scrollbar vertical hidden 但是 这似乎已被弃用 现在我收到错误Unknown configuration setting VsCode 中有隐藏滚动条的
  • LibGit2Sharp CheckoutPaths()

    我做了一次提交 49916 现在我想将提交的一个文件检出到工作目录中 该文件名为 NEW txt 如果我输入 Git 签出 49916 NEW txt 进入 Git Bash 后 它会创建 NEW txt 文件 其内容位于我的工作目录中 但
  • 非重叠串行端口挂在 CloseHandle 处

    我编写了一个自己开发的串行端口类 为了简单起见 我使用了阻塞 同步 不重叠 我浏览了所有 MSDN 文档 这对我来说很困难 我在从端口打开 传输或接收字节方面没有任何问题 所有操作都是同步并且不存在线程复杂性 function TSeria
  • Crystal Reports 11 - 添加无用的空白页,仅添加有数据的组标题

    我遇到了一个在使用 Crystal Reports 之前从未见过的奇怪问题 我为一家银行制作了一份复杂的 PDF 月度报告 生成了 200 多页 这些规范花了几个月的时间与客户进行调整 但现在它工作得很好 显示了所有应该显示的数据 所有数据
  • Kubernetes API 服务器日志中的 TLS 握手错误

    我正在研究一个AWS 中 Kubernetes 集群的 terraform 配置 https github com ericandrewlewis kubernetes via terraform 我已经让集群运行起来了 我可以通过 kub
  • 是否有通用方法将约束应用于类型应用程序?

    A comment https stackoverflow com questions 41111715 making a constraint of maybe a where eq a 41111825 noredirect 1 com
  • 不使用循环操作数组

    学习 VBA for Excel 我尝试在不使用循环的情况下完成尽可能多的编码 作为练习 将两个相邻范围的数字相乘 我想出了这个 Sub multiply range Dim a b c As Range Set a Range a1 a5
  • Polly 使用不同的请求主体重试请求

    我以前从未使用过 Polly 并且不确定这对 Polly 来说是否是一个好的场景 我正在调用一个列表为 1000 的端点DTO in the POST身体 现在端点将对每个执行一些验证DTO如果其中任何 DTO 验证失败 则返回 HTTP
  • 使用 WIF 在 .NET Web Farm 中为多个电子商务网站实施 SSO?

    我有一个我认为相当复杂的问题 所以我会尽力在这里阐明它 我正在寻找单点登录 SSO 解决方案 我知道有很多选择 但在我添加了它们需要满足的标准时排除了其中的大多数 以下是标准 1 SSO 必须添加到现有 系统 中 2 现有 系统 由 X 个
  • 谷歌是如何获得地图上邮政编码的轮廓的?

    例如 http g co maps 2dpkj http g co maps 2dpkj有邮政编码区域周围的轮廓 我知道这无法通过 API 获得 但我还可以从哪里获取此数据 例如 KML 格式 这是英国数据 最有可能的Google 与英国地
  • 外部 SQLite 文件内容访问错误

    我有以下代码 它给出了如下运行时错误 为什么 try String myPath DB PATH DB NAME mDB SQLiteDatabase openDatabase myPath null SQLiteDatabase OPEN
  • Push API 和服务器发送事件有什么区别?

    从文档中我可以看出Push API http w3c github io push api and 服务器发送事件 http www html5rocks com en tutorials eventsource basics 两者都是半双
  • 在没有源的情况下更改 .jar 文件?

    我有一个基于 Java 的 TCP 客户端 它与我们的生产服务器通信 我正在重写它 客户端对服务器的 IP 和端口进行硬编码 我想要做的就是将客户端中的 IP 地址更改为 127 0 0 1 我可以在我的开发盒上使用相同的端口号 问题是 我
  • 爪哇。隐式超级构造函数 Employee() 未定义。必须显式调用另一个构造函数[重复]

    这个问题在这里已经有答案了 您好 我是 Java 新手 我在生产工作线程类中遇到此错误 我的生产工作线程构造函数显示显式调用另一个构造函数 我不知道该怎么办 import java util Date public class Employ
  • Python 或 PHP 中的感知哈希算法? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 相同运算符优先级的结合性 -- *start++

    为什么会出现下面的表达式 total start 评估为 total start And not total start though this doesn t really matter either it would be the sa
  • 有 Delphi XE2 样式库吗?

    在 XE2 中 有一个新函数 styles 用于 VCL vsf 和 Firemonkey styles 有些是在C Program Files Embarcadero RAD Studio 9 0 Redist styles目录 创建新样
  • 同步视图模型和视图

    我有一个由一些节点和一些连接器组成的视图模型 public class ViewModel public List
  • 更改滚动时的 URL 哈希并保持后退按钮正常工作

    在具有固定顶部菜单和锚点导航的一页布局上 我有一个 scrollspy 它可以更改滚动时的片段标 识符 根据滚动位置为菜单链接提供一个活动类 并使用 Velocity js 将滚动动画到锚点 不幸的是 它还做了什么 当单击浏览器后退按钮时
  • 在 JavaScript 中递归构建 Promise 链 - 内存注意事项

    In 这个答案 https stackoverflow com a 29906627 3478010 递归地构建承诺链 稍微简化一下 我们有 function foo function doo always return a promise