当循环位于异步函数内部而不是相反时,为什么异步/等待正常工作?

时间:2019-03-24 12:11:11

标签: javascript async-await es6-promise

>>> git cherry-pick xyz fatal: bad object xyz await上时,我有三个摘要循环了三遍。

在第一个代码段中,它按我的预期工作,并且promise的值随每个i递减。

await

输出:

let i = 3;

(async () => {
  while (i) {
    await Promise.resolve();
    console.log(i);
    i--;
  }
})();

在第二个中,3 2 1 的值连续递减,直到达到零,然后执行所有i

await

输出:

let i = 3;

while (i) {
  (async () => {
    await Promise.resolve();
    console.log(i);
  })();
  i--;
}

最后,此错误会导致0 0 0 错误,并且不会显示任何值。

Allocation failed - JavaScript heap out of memory

有人可以解释为什么他们表现出这些不同的行为吗?谢谢。

4 个答案:

答案 0 :(得分:16)

关于第二个片段:

在不等待结果的情况下调用异步函数称为即发即弃。您告诉JavaScript它应该开始一些异步处理,但是您不在乎它何时以及如何完成。就是这样它循环,触发一些异步任务,当循环完成时,它们会在完成时返回,并在循环已经结束时记录为0。如果愿意:

await (async () => {
  await Promise.resolve();
  console.log(i);
})();

它将按顺序循环。

关于您的第三个片段:

您永远不会在循环中减少i,因此循环将永远运行。如果异步任务在某个时间执行,则会减少i,但这不会发生,因为while循环会疯狂运行并阻塞浏览器并使之崩溃。

 let i = 3;
 while(i > 0) {
   doStuff();
 }

答案 1 :(得分:9)

主要关注最后一个示例:

let i = 3;
while (i) {
  (async () => {
    await Promise.resolve();
    console.log(i);
    i--;
  })();
}

如果我们在不进行异步/等待的情况下重写代码以揭示其实际功能,则可能会有所帮助。在后台,异步功能的代码执行推迟到以后:

let callbacks = [];

let i = 0;
while (i > 0) {
  callbacks.push(() => {
    console.log(i);
    i--;
  });
}

callbacks.forEach(cb => {
  cb();
});

如您所见,直到循环完成后,才执行任何回调。由于循环永远不会停止,因此最终虚拟机将用光空间来存储回调。

答案 2 :(得分:2)

在您的特定示例中,它递减i,然后运行async代码,如:

let i = 3;

while (i) {
  i--; // <---------------------
  (async () => {            // |
    await Promise.resolve();// |
    console.log(i);         // |
  })();                     // |
 // >---------------------------
}

关于您的第三个代码段,它将永远不会降低i的值,因此循环将永远运行并因此导致应用程序崩溃:

let i = 3;
while (i) {
  (async () => {
    await Promise.resolve(); // await and resolve >-----------
    // the following code doesn't run after it resolves   // |
    console.log(i);                                       // |
    i--;                                                  // |
  })();                                                   // |
  // out from the (async() => {})() <-------------------------
}

答案 3 :(得分:2)

因为在第一种情况下,console.log和decre相互同步工作,因为它们都在同一个异步函数中。 在第二种情况下,console.log异步工作,而递减工作同步。 因此,递减将首先执行,异步函数将等待同步函数完成,然后使用i == 0

执行

在第三种情况下,循环体同步执行,并在每次迭代时运行异步功能。因此,递减直到循环结束才起作用,因此循环中的条件始终为真。这样,直到堆栈或内存已满