125

I have two functions, a and b, that are asynchronous, the former without await and the latter with await. They both log something to the console and return undefined. After calling either of the function, I log another message and look if the message is written before or after executing the body of the function.

function someMath() {
  for (let i = 0; i < 9000000; i++) { Math.sqrt(i**5) }
}

function timeout(n) {
   return new Promise(cb => setTimeout(cb, n))
}

// ------------------------------------------------- a (no await)
async function a() {
  someMath()
  console.log('in a (no await)')
}

// ---------------------------------------------------- b (await)
async function b() {
  await timeout(100)
  console.log('in b (await)')
}

clear.onclick = console.clear

aButton.onclick = function() {
  console.log('clicked on a button')
  a()
  console.log('after a (no await) call')
}

bButton.onclick = function() {
  console.log('clicked on b button')
  b()
  console.log('after b (await) call')
}
<button id="aButton">test without await (a)</button>
<button id="bButton">test with await (b)</button>
<button id="clear">clear console</button>

If you launch test without await, the function seems to work as if it was synchronous. But with await, the messages are inverted as the function is executed asynchronously.

How does JavaScript execute async functions when no await keyword is present?


Real use case: I have an await keyword which is conditionally executed, and I need to know if the function is executed synchronously or not in order to render my element:

async function initializeComponent(stuff) {
   if (stuff === undefined)
      stuff = await getStuff()
   // Initialize

   if (/* Context has been blocked */)
       renderComponent() // Render again if stuff had to be loaded
}

initializeComponent()
renderComponent()

P.S: The title has the JavaScript keyword to avoid confusion with the same questions in other languages (i.e Using async without await)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
  • You also need to convert `bButton.onclick` function into an async function and await for `b()`to end in order to get the desired log. – Jose Hermosilla Rodrigo Aug 09 '17 at 15:23
  • @JoseHermosillaRodrigo I don't want to wait for the desired log, I want to know with using or not using `await` keyword alters the synchronous function. And if it does not, maybe why my test is wrong. I'll update with my real use case at the end, maybe it'll be clearer for you. – Ulysse BN Aug 09 '17 at 15:24
  • 1
    Recommended reading - https://medium.com/javascript-in-plain-english/async-await-javascript-5038668ec6eb – arcseldon Aug 31 '19 at 13:27
  • 2
    The await keyword is what causes asynchronousness. If there is no await, then the statements are executed synchronously. However, if you want your function to return a value, then the async makes a difference. Without async, you just get a value; but with, you get a promise and you need to await the value, or get it in a .then() callback, which is asynchronous. IE, 'async' can make a difference to the caller of the function, even if there's no 'await'. – Max Waterman Aug 26 '22 at 16:37

4 Answers4

112

Mozilla documentation:

An async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value.

As you assumed, if no await is present, the execution is not paused and your code will then be executed synchronously as normal.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Karim
  • 8,454
  • 3
  • 25
  • 33
  • 11
    is there an eslint rule for redundant `async` keyword? I dont think synchronous functions should have it – phil294 Jan 17 '18 at 17:24
  • oh yes, thats the one. missed it somehow :D thank you – phil294 Jan 17 '18 at 18:57
  • 34
    One benefit to declaring 'async' in a function that returns a promise but doesn't use `await` is that exceptions will be rejected instead of synchronously thrown. e.g. `const result = someAsyncFunctionWithoutAwait().catch(handleRejection)` – aaaaaa Apr 30 '18 at 23:09
  • This is *not* the behavior I am seeing in Chrome... it seems like sometimes stuff that *should* be synchronous winds up happening after the async function returns if await is not used. – Michael Oct 06 '18 at 22:55
  • I don't get this answer... I just ran a simple experiment in Chrome's Dev Tools, and it turns out that calling a promise in a function that isn't marked `async` and not using `await`, doesn't block, as expected... So if it doesn't block, that means it's executed asynchronously, right? Here's the example: `f = () => new Promise((resolve, reject) => setTimeout(() => resolve(console.log('hello')), 2000));` and calling it like so `(() => { f(); console.log('world') })()` gives 'world' then 'undefined' then 'hello'... So, why would I ever want to mark a function `async` if I'm not using `await`? – user3773048 Mar 10 '19 at 07:51
  • 8
    In other words, shouldn't this answer say "if no await is present the execution is not paused, but your code will still be executed in a non-blocking manner" instead of "if no await is present the execution is not paused and your code will then be executed synchronously"? Furthermore, if no async and no await is present, your code will still be non-blocking (eg, declaring a function with the async keyword without await is equivalent to not declaring that same function with the async keyword). – user3773048 Mar 10 '19 at 08:00
  • @user3773048, i agree with your comment, i've edited my answer. thanks – Karim Mar 10 '20 at 17:08
  • No problem, @Karim. – user3773048 Mar 11 '20 at 18:55
  • @user3773048 Let's say you have a function B that is supposed to do a bunch of axios calls in a synchronous manner (because each calls depends on the other one or something). You need to use await in front of every axios call. But at the same time, it means you have to put async in front of B. If I want to be able to just trigger B (when you press some button let's say) from another function A (that's not async), you'll just call B and not use `await`. So here you go, `async` function without `await`. – Adrian Pop Nov 27 '20 at 07:58
  • 2
    A great way to look at it is that Async/Await allows you to *synchronize* async code: `async function dummy(){ /*...*/ return Promise.resolve(/*...*/); } async dummyAsync(){ dummy(); nextDummy(); } async dummySync(){ await dummy(); nextDummy(); }`. In **dummyAsync** nothing stops `nextDummy()` from executing before `dummy()` completes; its *asynchronous*. In ***dummySync*** `nextDummy()` does not execute until the promise from `dummy()` resolves (returns); it is *synchronous*, or *procedurally-written* asynchronous code. @AdrianPop gives a perfect use for declaring async & not calling await. – Rik Mar 13 '21 at 03:30
  • 1
    @user3773048 Normal code is executed in a blocking manner, not "non-blocking" – Bergi Jun 12 '22 at 05:22
  • @Karim does this mean an async function without await isn blocking instead non-blocking? because synchronous is synonym of blocking, isn't? – Jero Lopez Jan 24 '23 at 03:31
  • hi @Bergi +1, as soon as the first `await` is encountered, the execution of the `async` function body is stopped and put in the job queue, right? –  Feb 25 '23 at 18:19
  • and when the call stack is empty, JavaScript resumes execution, right? @Bergi –  Feb 25 '23 at 18:22
  • 1
    @GeorgeMeijer No, the execution is stopped when the `await` is encountered, the resumption is only put in the job queue once the awaited promise is fulfilled. – Bergi Feb 25 '23 at 23:53
72

Everything is synchronous until a Javascript asynchronous function is executed. In using async-await await is asynchronous and everything after await is placed in event queue. Similar to .then().

To better explain take this example:

function main() {
  return new Promise( resolve => {
    console.log(3);
    resolve(4);
    console.log(5);
  });
}

async function f(){
    console.log(2);
    let r = await main();
    console.log(r);
}

console.log(1);
f();
console.log(6);

As await is asynchronous and rest all is synchronous including promise thus output is

1
2
3
5
6
// Async happened, await for main()
4

Similar behavior for main() without promise too:

function main() {
    console.log(3);
    return 4;
}

async function f(){
    console.log(2);
    let r = await main();
    console.log(r);
}

console.log(1);
f();
console.log(5);

Output:

1
2
3
5
// Asynchronous happened, await for main()
4

Just removing await will make whole async function synchronous which it is.

function main() {
    console.log(3);
    return 4;
}

async function f(){
    console.log(2);
    let r = main();
    console.log(r);
}

console.log(1);
f();
console.log(5);

Output:

1
2
3
4
5
Inigo
  • 12,186
  • 5
  • 41
  • 70
NAVIN
  • 3,193
  • 4
  • 19
  • 32
  • 2
    You could add also a function that does var g = await f() since f() is async and perhaps would need to be awaited on a long running process if you dont want to block the main executing thread. – flux9998 Jan 04 '19 at 00:05
  • 1
    `await` can only be used inside `async` function. – NAVIN Mar 07 '19 at 05:44
  • 3
    **Please Note this is old implementation before ES11. In ES11 things have changed little bit. But this will still work.** – NAVIN Jul 30 '20 at 17:16
  • 4
    is it always the case that the code in the event queue (everything after **await**) runs after the main thread finishes? What I mean is that, in two of your examples, after your last `console.log` the Event Queue runs, and the code that was being "awaited" finally executes. If we had more instructions and more code, will it still be the case that after the very last instruction the awaited code runs? – chris Dec 11 '20 at 14:51
  • The last example should have a promise in `main()`, and it won't be synchronous. So removing await doesn't necessarily make execution synchronous. – user2734550 Sep 19 '22 at 00:24
38

The function is executed the same with or without await. What await does is automatically wait for the promise that's returned by the function to be resolved.

await timeout(1000);
more code here;

is roughly equivalent to:

timeout(1000).then(function() {
    more code here;
});

The async function declaration simply makes the function automatically return a promise that's resolved when the function returns.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 1
    Useful answer - for a longer explanation perhaps also try this blog - https://medium.com/javascript-in-plain-english/async-await-javascript-5038668ec6eb – arcseldon Aug 31 '19 at 13:29
18

As other answers say/indicate: an async function just runs on spot until it encounters an await - if there is no await, it runs completely.

What may be worth adding that async unconditionally makes your result a Promise. So if you return something, there is a difference already and you simply can not get the result without returning to the JS engine first (similarly to event handling):

async function four(){
  console.log("  I am four");
  return 4;
}
console.log(1);
let result=four();
console.log(2,"It is not four:",result,"Is it a promise ?", result instanceof Promise);
result.then(function(x){console.log(x,"(from then)");});
console.log(3);
tevemadar
  • 12,389
  • 3
  • 21
  • 49
  • @UlysseBN I think I found the middle-ground :-). While the `{}` appearing here is not very spectacular, it already shows that it is not `4` for sure, and it may be worth mentioning that in the JS console of the browser one can see and look into the actual object. – tevemadar Apr 09 '20 at 15:20
  • yeah I guessed that was what you expected! Unfortunately I don't know a good way to show the class an instance is from in JS... (but `instanceof`) – Ulysse BN Apr 09 '20 at 15:46
  • So, while the code inside async function 'four()' runs synchronously, to get the return value (4), the caller needs to do so asynchronously, using 'await' or '.then()'. It is quite different in some scenarios, right? – Max Waterman Aug 26 '22 at 16:22
  • @MaxWaterman yes, of course. Remember that the context of every single answer here is the orginal question: what happens if someone writes an `async` function, without using `await` inside. That's simply not very useful in real-life code, so practically any real-life code will be quite different. – tevemadar Aug 27 '22 at 08:28
  • I dont get why the "4 (from then)" line gets printed after the 3. Why is this the case? – Aeternus Jan 26 '23 at 00:19
  • Because `Promise` completion is practically an event, and the `then(...)` is its event handler. The JavaScript engine processes events only after the currently running code returned to it (that's one of the classic recipes for a frozen webpage: write a long-running event handler, so other events can't be processed). – tevemadar Jan 26 '23 at 10:02