JavaScript堆如何处理递归

时间:2018-07-27 23:32:07

标签: javascript node.js asynchronous recursion heap

您好,我在上面的问题有点含糊,因此我将尝试使其更加清晰。

我有以下形式的代码:

function main () {

  async function recursive () {
     var a = "Hello World";
     var b = "Goodbye World";

     recursive();

  }
  recursive();

}

我遇到堆内存不足的问题。

假设上面显示的是程序的行为方式,并且在递归函数中声明了a和b,我的问题是变量是否在递归函数中调用递归时被销毁,或者它们是否会持续存在假设我保持主函数运行足够长的时间,以免发生递归调用,并且主函数到达其端点。

我担心它们仍然存在于堆中,并且由于我的真实程序在这些变量中存储了大字符串,因此我担心这就是我耗尽堆的原因。

1 个答案:

答案 0 :(得分:7)

JavaScript还没有(?)具有用于递归的尾调用优化,因此,可以肯定的是,您最终将填满您的调用堆栈。堆被填满的原因是因为recursive被定义为async函数,并且从不等待分配的Promise对象解析。这将填满您的,因为垃圾收集器没有机会收集它们。

简而言之,除非您要在函数中的堆中分配内容,否则递归不会耗尽堆。

扩展正在发生的事情:

main
  new Promise(() => {
    new Promise(() => {
      new Promise(() => {
        new Promise(() => {
          new Promise(() => { //etc.

您说您不是在等待Promises,因为您希望事物并行运行。由于某些原因,这是一种危险的方法。万一抛出承诺,应该始终等待它们,当然,您要确保分配的内存使用量是可预测的。如果此递归函数意味着您获得了无法预测的所需任务数量,则应考虑任务队列模式:

const tasks = [];

async function recursive() {
  // ...
  tasks.push(workItem);
  // ...
}

async function processTasks() {
  while (tasks.length) {
    workItem = tasks.unshift();

    // Process work item. May provoke calls to `recursive`
    // ...do awaits here for the async parts
  }
}

async function processWork(maxParallelism) {
  const workers = [];
  for (let i = 0; i < maxParallelism; i++) {
    workers.push(processTasks());
  }

  await Promise.all(workers);
}

最后,如果需要说一句,async函数不允许您进行并行处理。它们只是提供了一种更方便地编写Promises并进行顺序和/或并行等待异步事件的方式。除非您使用Web Worker之类的语言,否则JavaScript仍然是单线程执行引擎。因此,使用异步函数尝试并行化同步任务对您无济于事。