在这个答案中,一个promise链是递归构建的。
略有简化,我们有:
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
}
据推测,这将产生一个调用栈 和 一个promise链,即“深”和“宽”。
我预计内存峰值将比执行递归或单独建立承诺链更大。
调用堆栈和承诺链-即“深”和“宽”。
其实没有
据我们所知,这里没有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实现
问题内容: 我想遍历HTML 5文件系统中的所有文件,并在迭代完成后开始一些事件。由于这是异步+承诺,我很难尝试掌握其工作方式。 我正在使用angularJS,并创建了一个服务来封装html 5文件系统特定的功能。 这是递归函数: 理想情况下,我想这样调用该函数,并让其返回一个承诺,一旦遍历所有文件,该承诺便会执行。 有什么技巧/想法可以实现吗? 一个想法是拥有一个诺言数组,并为每个文件/目录向该
问题内容: 我有一个像这样的递归函数 我正在使用它 我注意到您好永远不会返回,因为我怀疑我在递归调用上创建了多个promise,但是我不确定如何从中返回。 如何返回每个递归创建的Promise? 编辑: 结果是 问题答案: 递归是一种功能性遗产,因此将其与功能性样式一起使用可产生最佳效果。这意味着编写接受和操作其输入(而不是依赖于外部状态)和返回值(而不是依赖于突变或副作用)的函数。 你的程序,而
问题内容: 我有一个整数id的数组,例如 并且我需要为每个ID执行异步远程调用。每个调用都是一个使用$ resource执行的WebAPI请求,并显示为Promise。 我需要创建一个接受这些ID数组的函数,然后初始化递归承诺链。该链应导致对每个ID依次进行webapi调用。这些调用不应并行,而应链接在一起。 有问题的函数返回自己一个“主要”的承诺,该承诺应根据异步Web调用的结果来解决或拒绝。也
请检查下面的反转功能。剩下的代码应该没问题。由于某种原因,该函数没有反转双链接列表。 双链表节点结构 双链表结构 按从头部到尾部的顺序排列。 请检查下面的反向函数,因为此函数不会返回反向双链接列表。检查是否有任何错误并让我知道。
问题内容: 我正在做的事情涉及按顺序运行(进行一些设置,然后运行调用者感兴趣的实际命令),然后进行一些清理)。 就像是: 哪里是这样的: 在内部,我使用,它返回,并且有效地将事件的结果从返回给调用者。 现在,我还需要将事件从stdout和stderr发送到调用者,这些事件也来自EventEmitters。是否有一种方法可以使(Bluebird)Promises正常工作,或者它们只是妨碍发出多个事件
问题内容: 问题1:在给定的时间只允许一个API请求,因此,真正的网络请求在尚未完成的情况下排队。应用可以随时调用API级别,并且期望得到回报。当API调用排入队列时,将在将来的某个时刻创建对网络请求的承诺- 返回什么给应用程序?这样可以通过延迟的“代理”承诺来解决: 问题2:对一些API调用进行反跳动,以便随着时间的推移累积要发送的数据,然后在达到超时时分批发送。调用API的应用期望得到回报。