在JavaScript中以递归方式构建承诺链-内存注意事项

在JavaScript中以递归方式构建承诺链-内存注意事项,第1张

在JavaScript中以递归方式构建承诺链-内存注意事项

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

其实没有
据我们所知,这里没有promise链

doSomeThingAsynchronous.then(doSomethingAsynchronous).then(doSomethingAsynchronous).…
(如果以这种方式编写,则按顺序执行处理程序是
Promise.each
Promise.reduce
可能做的事情)。

我们在这里面对的是一个 _解决链_1-当满足递归的基本情况时,最终会发生类似的事情

Promise.resolve(Promise.resolve(Promise.resolve(…)))
。如果您要称呼它,那只是“深”,而不是“宽”。

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

实际上不是峰值。随着时间的流逝,您会慢慢建立大量的承诺,并用最内层的承诺来解决,所有承诺都代表相同的结果。在任务结束时,当条件满足且最内层的承诺以实际值解决时,所有这些承诺都应以相同的值解决。这最终将

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

是这样吗

不必要。如上所述,该批量中的所有promise最终都使用相同的值2进行解析,因此我们需要的是一次存储最外部和最内部的promise。所有中间的Promise可能会尽快变为垃圾回收,我们希望在恒定的空间和时间中运行此递归。

实际上,对于具有动态条件(无固定步数)的异步循环,此递归构造是完全必要的,您无法避免。在Haskell中,

IO
monad
一直使用该代码,仅由于这种情况而对其进行了优化。它与尾调用递归非常相似,后者通常被编译器消除。

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

是。这是在承诺/ Aplus的讨论,例如,虽然没有结局呢。

许多承诺库确实支持迭代助手,以避免

then
诸如Bluebird
each
map
方法之类的承诺链尖峰。

我自己的诺言库3,4确实具有解析链,而没有引入内存或运行时开销。当一个承诺采纳另一个承诺(即使仍未完成)时,它们就变得难以区分,中间的承诺不再在任何地方被引用。

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

是。尽管这种情况可以优化,但很少如此。具体来说,ES6规范确实要求Promises在每次

resolve
通话时检查该值,因此无法折叠链。甚至可以用不同的值来解决链中的承诺(通过构造一个滥用吸气剂的示例对象,而不是现实生活中的对象)。该问题是在esdiscuss上提出的,但仍未解决。

因此,如果使用泄漏的实现,但需要异步递归,则最好切换回回调并使用延迟的反模式将最内层的Promise结果传播到单个结果Promise。

[1]:没有官方术语
[2]:嗯,它们是相互解决的。但是,我们 希望 用相同的值来解决这些问题,我们 期待 的是
[3]:无证 *** 场,通过Aplus的。阅读代码的后果自负 :
[4]:在此pull请求中也为Creed实现



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5675717.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存