-1

I have wrote the following code:

async function imaAsyncFunction () {
    console.log('1234');
    let res = await setTimeout(()=>{console.log('10 sec');},10000);
    console.log(res);
    console.log('is After?!?');
}

imaAsyncFunction();
console.log('bla bla bla');

I was a little surprise that '1234' isn't printed at all. In addition the line console.log(res); was printed before the 10 seconds of timeout was finished, but i guess that the reason is that the code after await doesn't return a Promise. But, why does the code : console.log('bla bla bla'); was printed before both lines:

  console.log(res);
  console.log('is After?!?');

If we saw that the code doesn't postpone because of the setTimeout() so why both line of codes don't run before the function is finished and only after that the console.log('bla bla bla'); should run? What am I missing here?

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Eitanos30
  • 1,331
  • 11
  • 19
  • Does this answer your question? [Combination of async function + await + setTimeout](https://stackoverflow.com/questions/33289726/combination-of-async-function-await-settimeout) – JSON Derulo Jan 12 '21 at 11:52
  • *"Does all async function code will run in a different thread?"* No, as far as I know, JS is single threaded – Cid Jan 12 '21 at 11:57
  • @JSONDerulo wish I could upvote your user name – phuzi Jan 12 '21 at 11:58
  • 1
    `setTimeout` does not return a Promise. So there's no point using `await setTimeout`. – Jeremy Thille Jan 12 '21 at 12:07
  • 1
    *"I was a little surprise that '1234' isn't printed at all."* It is. I've copied your code into a Stack Snippet. As you can see, the `1234` is logged. – T.J. Crowder Jan 12 '21 at 12:51
  • T.J. Crowder.. i see.. interesting :).. thank you.. If i will read the chapter in your book do you think that i will understand why the `bla bla bla` is printed before the two lines? and why when printing `res` i'm not getting `undefined`? – Eitanos30 Jan 12 '21 at 13:03

1 Answers1

3

Does all async function code will run i a different thread?

No. async functions, await, and promises do not create or use different threads.

A promise doesn't make anything asynchronous on its own¹, it's just a way of observing the completion of something that's already asynchronous (like your timer callback, which uses the timer subsystem of your environment to call your timer callback later).

async functions are "just" a way (a really, really useful way) to wait for promises to settle via syntax rather than by attaching fulfillment and rejection handlers using the .then or .catch methods.

I was a little surprise that the line console.log(res); was printed before the 10 seconds of timeout was finished, but i guess that the reason is that the code after await doesn't return a Promise.

setTimeout doesn't return a promise, right. So await doesn't wait for the timer callback to occur. See this question's answers for how you'd make a promise-enabled version of setTimeout.

But, why does the code : console.log('bla bla bla'); was printed before both lines:

For the same reason: Your async function returns a promise, and your code calling your async function doesn't wait for that promise to be settled before moving on to the console.log('bla bla bla') line.

Here's code that both uses a promise-enabled version of setTimeout and waits for your async function's promise to settle before doing the console.log:

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function imaAsyncFunction () {
    let res = await delay(800); // 0.8s instead of 10s
    console.log("Timer expired");
    console.log(res); // undefined, since we haven't set a fulfillment value
    console.log("is after");
}

imaAsyncFunction()
.then(() => {
    console.log("bla bla bla");
})
.catch(error => {
    console.error(error);
});

If you wanted the delay function to return a fulfillment value, we could do this:

function delay(ms, value) {
    return new Promise(resolve => setTimeout(() => {
        resolve(value);
    }, ms));
}

although in all standard environments (now), setTimeout accepts a value to pass to its callback, so we can do just:

function delay(ms, value) {
    return new Promise(resolve => setTimeout(resolve, ms, value));
}

function delay(ms, value) {
return new Promise(resolve => setTimeout(resolve, ms, value));
}
async function imaAsyncFunction () {
    let res = await delay(800, 42); // 0.8s instead of 10s
    console.log("Timer expired");
    console.log(res); // 42
    console.log("is after");
}

imaAsyncFunction()
.then(() => {
    console.log("bla bla bla");
})
.catch(error => {
    console.error(error);
});

In a comment you've said:

i still can't understand why both lines doesn't runs before the `console.log('bla bla bla');?

Here's your original code:

async function imaAsyncFunction () {
    console.log('1234');
    let res = await setTimeout(()=>{console.log('10 sec');},10000);
    console.log(res);
    console.log('is After?!?');
}

imaAsyncFunction();
console.log('bla bla bla');

When you run that code, here's what happens, in order:

  1. The function imaAsyncFunction is created.
  2. The call to imaAsyncFunction() is executed:
    1. The synchronous part of the function is run:
      • console.log('1234'); is executed
      • setTimeout(()=>{console.log('10 sec');},10000) is executed
      • setTimeout returns undefined
    2. The await is reached: (Note: I'm skipping some details below for clarity.)
      • Normally, when you do await p, p is a promise (or at least something like a promise). Since you've used it on undefined instead, await wraps undefined in a promise as though it called Promise.resolve(undefined) and used that as p instead of undefined.
      • All of the code that follows await in the function is attached to p as a handler for when p is fulfilled (since you haven't used try/catch — which is fine — so there's no rejection handler). When you attach a fulfillment or rejection handler to a promise, you create a new promise that gets settled based on what happens to the original promise and, if relevant, what happens when the fulfillment or rejection handler is called. Let's call that new promise p2.
      • Usually, p wouldn't be settled yet, but in this case it is, so the fulfillment handler (the rest of the code in imaAsyncFunction, after the await) is scheduled to run once the current synchronous work is completed. If p weren't settled, this wouldn't happen yet (it would happen later, when p is settled.)
      • Since there aren't any other await expressions in the function, the promise imaAsyncFunction implicitly created (let's call it p3) is resolved to p2. That means that when p2 settles, the promise from imaAsyncFunction is settled in the same way (it's fulfilled with the same fulfillment value as p2, or rejected with the rejection reason from p2).
      • imaAsyncFunction returns p3.
  3. The call to imaAsyncFunction is done, so the next statement is executed: console.log('bla bla bla');
  4. Code execution reaches the end of the script, which means all of the synchronous code has finished running.
  5. The fulfillment handler code scheduled in Step 2.2 above is run:
    1. It executes the console.log(res) and console.log('is After?!?').
    2. It fufills p2 with undefined (since there's no return x in the function).
      • This schedules the fulfillment of p3, since p3 was resolved to p2. Doing that would scheduled execution of p3's fulfillment handlers, but it doesn't have any — nothing used await or .then or .catch, etc., on imaAsyncFunction()'s return value.

¹ There's one caveat to that statement: When you attach a fulfillment or rejection handler to a promise, the call to that handler is always asynchronous even if the promise is already settled. That's because it would be asynchronous if the promise weren't settled, and it would be chaotic if the call to your handler was synchronous in some situations but asynchronous in others, so it's always done asynchronously. But that's literally the only thing about promises or async functions that makes anything asynchronous, the rest just observes things that are already asynchronous.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • i can't understand the : `For the same reason. Your async function returns a promise, and your code calling your async function doesn't wait for that promise to be settled before moving on to the console.log('bla bla bla') line.` Still can't understand why both lines won't be run before the `console.log('bla bla bla');` – Eitanos30 Jan 12 '21 at 11:57
  • @Eitanos30 - When you call an `async` function, only the code in the function before the first `await` or `return` (implicit or explicit) is run synchronously. At that point, the `async` function returns a promise, and the rest of its code runs asynchronously. The promise is settled depending on what happens in that code. Since `imaAsyncFunction();` doesn't do anything to wait for the promise to be settled, the next line of code is run. – T.J. Crowder Jan 12 '21 at 12:00
  • FWIW, I go into promises in a fair bit of detail in Chapter 8 of my recent book, *JavaScript: The New Toys*. And then I continue with `async`/`await` in Chapter 9. Links in my profile if you're interested. – T.J. Crowder Jan 12 '21 at 12:04
  • Crowder, if i'm adding a first line before the await, so the console.log('bla bla bla') won't run at all – Eitanos30 Jan 12 '21 at 12:08
  • @Eitanos30 - If you look in the browser console, you'll see an error. You can only use `await` A) inside an `async` function, or B) in a module **if** your environment supports [top-level `await`](https://github.com/tc39/proposal-top-level-await) in modules. That's why I didn't use `await` in my example, I used `.then` and `.catch` instead. – T.J. Crowder Jan 12 '21 at 12:09
  • Crowder, first of all thanks a lot.. Secondly, i have edited my question, i will be glad if you look again. Thirdly, maybe my English is poor, and therefore i still can't understand why both lines doesn't runs before the `console.log('bla bla bla');? Is it because any priority issue? Fourthly, i am approving your answer because i'm sure you are right and it may help other people that understand js better than me – Eitanos30 Jan 12 '21 at 12:31
  • @Eitanos30 - I've added an explanation at the end of the answer for what happens with your latest code. I hope that helps! Happy coding! – T.J. Crowder Jan 12 '21 at 13:33
  • Crowder, great addition that helps me a lot. But i can't understand from where the p3 came? `imaAsyncFunction` returns implicity p2. Nothing is being done with this returned p2. So from where p3 came to the world? :) And why when running the snipping code i get '1' for `console.log(res)` and not `undefined`? – Eitanos30 Jan 12 '21 at 14:02
  • @Eitanos30 `imaAsyncFunction` implicitly returns `p3`, not `p2`. But `p3` is *resolved to* `p2` so that doesn't make a big difference. (If there were another `await`, the distinction between `p2` and `p3` would probably be clearer.) – T.J. Crowder Jan 12 '21 at 14:06