Node.js:异步函数中的尾部调用是否有优化?

标签 node.js recursion tail-recursion

我使用的是 Node v8.10.0

上述问题解释了 Node.js 如何不再支持 TCO。我最近遇到了这样的函数问题:

async function processBatch(nthBatch) {
    // do a bunch of async work and build up variables
    await processBatch(nthBatch + 1);
}

该代码存在内存泄漏,通过将其更改为:

async function processBatch(nthBatch) {
    // do a bunch of async work and build up variables
    return processBatch(nthBatch + 1);
}

我很惊讶这实际上有效,因为在上面描述的问题中,它清楚地解释了 Node 8.x 不支持 TCO。那么,是否有什么特殊的事情可以实现 TCO?或者是因为它在引擎盖下使用了生成器,并且返回将生成器标记为已完成,因此可以丢弃堆栈?

最佳答案

async function processBatch(nthBatch) {
    // do a bunch of async work and build up variables
    await processBatch(nthBatch + 1);
}

此代码片段会导致内存泄漏,因为注释中声明的变量无法被垃圾回收,因为解释器不知道它们不会再次被需要。例如解释器此时并不知道 await 之后没有一行可能需要所有这些声明的变量。

async function processBatch(nthBatch) {
    // do a bunch of async work and build up variables
    return processBatch(nthBatch + 1);
}

在这个例子中,函数被返回,因此垃圾收集器可以安全地清理方法中声明的变量。请注意,堆栈会保留下来,如果此递归函数迭代次数过多,则会抛出超出最大调用堆栈大小错误,但声明的变量位于堆中,因此可以在保持堆栈信息完整。

关于Node.js:异步函数中的尾部调用是否有优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49165805/

相关文章:

algorithm - Elixir 中二维列表的排列

c# - 生成尾调用操作码

ruby - ruby 中的尾递归——这两种实现有什么区别?

javascript - 在 grunt.util.spawn 中调用 grunt.config.set 似乎没有任何效果

java.lang.stackoverflow错误递归

c++ - 什么是可重入函数?

algorithm - 将线性递归函数重写为尾递归函数

javascript - Node .js。为什么页面无法加载 css 和 javascript 文件?

javascript - Pusher.subscribe() 不是服务器端的函数?

Node.js + 处理(.js)