异步递归函数没有参数会泄漏内存或导致堆栈溢出吗?

4
我有一个异步任务需要执行很长时间,并且需要重复执行。
简单的解决方案是:
const timeoutPromise = delay => new Promise(resolve => setTimeout(resolve, delay));

async function infiniteRecursiveLoop() {
  await longLastingWork();
  await timeoutPromise(10000);  // each time work is done, wait 10s and then run it again
  infiniteRecursiveLoop();
}

由于我没有使用任何参数并且没有等待递归调用的结果,所以这应该不会泄漏任何内存,也不会导致堆栈溢出,对吗?因为我完全不确定。

我需要在最近的Firefox和Chrome中使用它。

编辑:
实际上我只需使用while(true) {...},没有必要使用递归:)。但是我仍然想知道。

编辑2:
经过更多测试的版本:

(async function infiniteRecursiveLoop() {
  await Promise.resolve();
  infiniteRecursiveLoop();
})();

看起来这是特定于环境的:

  • NodeJS 12 - 没问题
  • Chrome 77 - 没问题
  • Firefox 70 - 存在内存泄漏,但没有堆栈溢出

但为什么呢...?


setInterval有什么问题? - Liam
@Liam setInterval 不会等待长时间任务的执行。在我的情况下,该任务经常需要比间隔时间更长的时间才能完成。 - icl7126
你觉得程序中出现无限递归没有问题吗?即使只是不好的习惯? - Michael
异步等待(async await)和什么有关系呢?递归就是递归,它会在 Firefox 中导致内存泄漏,这表明 Chrome 和 Node 必须采取特殊措施来处理这种不愉快的情况。这不是内存问题,而是调用栈问题。无限的调用栈。 - Michael
@icl7126 我建议您根据您的edit2重新提出这个问题,因为那是一个非常有趣的结果,一些更聪明的人可以解释为什么它在Chrome和Node中有效。对于您在此处的目的来说,最重要的一点是递归不适用于您的用例,会使代码更加复杂,并且在某些情况下可能会导致内存泄漏。对于您的问题,使用递归没有任何优势。 - Michael
显示剩余3条评论
1个回答

0

它是无限递归的,因此会导致错误。

由于您想要等待十秒钟以进行下一次调用,因此请在setTimeout函数中将下一次调用设置为infiniteRecursiveLoop。

async function infiniteRecursiveLoop() {
  await longLastingWork();
  setTimeout(infiniteRecursiveLoop, 10000);
}

这可能会阐明问题:

有关nodejs无限循环函数执行的奇怪观察

虽然在某些情况下可能可以做到这一点,但是使用不必要的无限递归绝对是不好的实践。


是的,这看起来不那么危险了。但是我不确定我的代码是否会失败。我刚试图在 NodeJS 中执行它,并且它似乎是一个很漂亮的无限循环而没有内存泄漏:(async function infiniteRecursiveLoop() { await Promise.resolve(); infiniteRecursiveLoop(); })(); - icl7126
这看起来可能很好,但那是因为它的工作速度非常慢。最终,它会导致堆栈溢出,因为这就是无限递归的工作原理。此外,在这个例子中,你实际上并不需要递归。你需要的是一个连续调用函数的循环。递归对于这种用例完全不合适。 - Michael
你认为这会导致“错误”?具体是哪种错误? - Liam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接