简体   繁体   中英

Why does Async/Await work properly when the loop is inside the async function and not the other way around?

I have three snippets that loop three times while await ing on a promise .

In the first snippet, it works as I expect and the value of i is decremented with each await .

let i = 3;

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

Output:

3
2
1

In the second one, the value of i is continuously decremented until it reaches zero and then all the await s are executed.

let i = 3;

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

Output:

0
0
0

Lastly, this one causes an Allocation failed - JavaScript heap out of memory error and doesn't print any values.

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

Can someone explain why they exhibit these different behaviors? Thanks.

Concerning your second snippet:

Caling an async function without awaiting it's result is called fire and forget . You tell JavaScript that it should start some asynchronous processing, but you do not care when and how it finishes. That's what happens. It loops, fires some asynchronous tasks, when the loop is done they somewhen finish, and will log 0 as the loop already reached its end. If you'd do:

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

it will loop in order.

Concerning your third snippet:

You never decrement i in the loop, therefore the loop runs forever. It would decrement i if the asynchronous tasks where executed somewhen, but that does not happen as the while loop runs crazy and blocks & crashes the browser.

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

Focusing primarily on the last example:

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

It may help if we rewrite the code without async/await to reveal what it is really doing. Under the hood, the code execution of the async function is deferred for later:

let callbacks = [];

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

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

As you can see, none of the callbacks are executed until after the loop is completed. Since the loop never halts, eventually the vm will run out of space to store callbacks.

In your particular example it decrements the i and then runs the async code like:

let i = 3;

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

Regarding your third snippet, it will never decrease the i value and so loop runs forever and thus crashes application:

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() => {})() <-------------------------
}

Because in the first case console.log and decrement work in sync with each other because they are both inside the same asnychronous function. In the second case console.log works asynchronously and decrement works synchronously. Therefore, the decrement will be executed first, the asynchronous function will wait for the synchronous function to finish, then it will be executed with i == 0

In the third case, the loop body executes synchronously, and runs the asynchronous function at each iteration. Therefore, the decrement can not work until the end of the loop, so the condition in the cycle is always true. And so until the stack or memory is full

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM