3

I thought I understood how the async/await works in Javascript until I ran the code:

let flag = false;

function test() {
  [1, 2, 3].map(async n => {
    console.log(`flag set at ${n} before if?`, flag);
    if (!flag) {
      const x = await a(n);
      // do something with x
      flag = x;
      console.log('flag set!');
    }
    console.log(`flag set at ${n} after if?`, flag);
  });
}

function a(n) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve(n);
    });
  });
}

test();

The actual output is:

flag set at 1 before if? false
flag set at 2 before if? false
flag set at 3 before if? false
flag set!
flag set at 1 after if? 1
flag set!
flag set at 2 after if? 2
flag set!
flag set at 3 after if? 3

Which is very different from what I thought it should be:

flag set at 1 before if? false
flag set!
flag set at 1 after if? 1
flag set at 2 before if? 1
flag set at 2 after if? 1
flag set at 3 before if? 1
flag set at 3 after if? 1

I think I need to be educated. Thanks.

Update: Thanks for the comments mentioning about the map. When I changed my code to be the following, it worked as expected:

let flag = false;

async function test() {
  for (const n of [1, 2, 3]) {
    console.log(`flag set at ${n} before if?`, flag);
    if (!flag) {
      const x = await a(n);
      // do something with x
      flag = x;
      console.log('flag set!');
    }
    console.log(`flag set at ${n} after if?`, flag);
  }
}

function a(n) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve(n);
    });
  });
}

test();
  • 5
    `map` does not await, it simply turns your original array into an array of promises. – zerkms Jul 30 '19 at 20:41
  • 1
    For a sequential loop, use `for of` around the `await`. `map` just calls the callback function three times, and each of the three created promises is independent. – Bergi Jul 30 '19 at 20:43
  • 1
    I am pretty sure it has to do with the use of map. If you were to use a for loop, I don't think you would have this output. – Kevin Pastor Jul 30 '19 at 20:43
  • Also if you don’t care about order when promises resolve you can use map and then await Promise.all(... to wait until they are all resolved – Jani Siivola Jul 30 '19 at 20:51
  • As a small critique, you forgot to pass in the second argument in your `setTimeout`. That promise is going to `resolve` immediately, so you might as well take it out if that's your intended behavior – Andrew Jul 30 '19 at 20:57
  • 2
    @Andrew Even though `setTimeout` calls the callback "immediately", it's not synchronous but [some minimum value](https://stackoverflow.com/q/9647215/1048572) - and its on the macro task loop, so being delayed after all promise stuff for sure. – Bergi Jul 30 '19 at 20:59
  • 2
    calling a setTimeout without a second argument is still going to be different than no setTimeout at all, as it will be put on the bottom of the browser async event queue ! – Morphyish Jul 30 '19 at 21:00
  • Yea, that's true. I know about `setTimeout` and that it goes to the event queue, but didn't really think about the minimum wait value that's associated with it – Andrew Jul 30 '19 at 21:02

1 Answers1

1

The await keyword make sure the current method thread wait for the function call to resolve before continuing.

Which is why every flag set! is printing before the after log.

However your execution is encapsulated in an asynchronous arrow function inside a .map. That means that, even if the arrow function itself is paused, the .map is not. As mentioned by @zerkms, it turns your array into an array of promises.

Think of it as an actual asynchronous call to a distant API. You aren't going to wait for your server to be done, all the calls from the .map will be done, and then evaluated as they come back for each initial value.

This is exactly what is happening here.

Morphyish
  • 3,932
  • 8
  • 26
  • 1
    I'm not saying that this answer is wrong, but some parts of it looks misleading, like "However your execution is encapsulated in the arrow function". Being an arrow function means adds to the problem. – zerkms Jul 30 '19 at 20:52
  • Would you have a suggestion on how to formulate it better so it isn't misleading ? English isn't my first language so I might not always word things correctly :( – Morphyish Jul 30 '19 at 21:02
  • I reworded this passage using @zerkms explanation to hopefully clear things up. – Morphyish Jul 30 '19 at 21:11