JavaScript堆如何处理递归

120*_*999 5 javascript heap recursion asynchronous node.js

您好我上面的问题有点模糊,所以我会尽量让它更清晰.

我有以下形式的代码:

function main () {

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

     recursive();

  }
  recursive();

}
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是我的堆内存不足.

假设我上面展示的是我的程序行为如何,在递归函数中声明a和b,我的问题是在递归函数中调用递归时是否销毁变量,或者它们是否会持续存在直到不再递归为止调用和main函数到达其端点,假设我保持主函数运行足够长时间以使其发生.

我担心他们在堆中活着,因为我的真实程序在这些变量中存储大字符串,我担心这就是我用完堆的原因.

Jac*_*cob 7

JavaScript(但是?)还没有针对递归的尾调用优化,所以肯定你最终会填满你的调用堆栈.堆填充的原因是因为recursive定义为async函数而且永远不会等待分配的Promise对象解析.这将填满你的堆,因为垃圾收集器没有机会收集它们.

简而言之,除非你在函数中分配堆中的东西,否则递归不会耗尽堆.

扩展正在发生的事情:

main
  new Promise(() => {
    new Promise(() => {
      new Promise(() => {
        new Promise(() => {
          new Promise(() => { //etc.
Run Code Online (Sandbox Code Playgroud)

你说你不是在等待Promise因为你想让事情并行运行.出于某些原因,这是一种危险的方法.应该总是等待Promise,以防它们抛出,当然你想确保你分配可预测的内存使用量.如果这个递归函数意味着你得到了一个不可预测的所需任务,你应该考虑一个任务队列模式:

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);
}
Run Code Online (Sandbox Code Playgroud)

最后,如果需要说,async函数不允许你进行并行处理; 它们只是提供了一种更方便地编写Promise并对异步事件执行顺序和/或并行等待的方法.除非你使用像web worker这样的东西,否则JavaScript仍然是单线程执行引擎.因此,使用异步函数尝试并行化同步任务将不会为您做任何事情.