Async function without await in JavaScript
Asked Answered
G

4

138

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)

Gaby answered 9/8, 2017 at 15:15 Comment(4)
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.Doubloon
@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.Gaby
Recommended reading - medium.com/javascript-in-plain-english/…Account
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'.Mellissamellitz
P
117

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.

Pithy answered 9/8, 2017 at 15:27 Comment(15)
is there an eslint rule for redundant async keyword? I dont think synchronous functions should have itFermium
oh yes, thats the one. missed it somehow :D thank youFermium
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)Floorman
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.Foe
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?Aleron
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).Aleron
@user3773048, i agree with your comment, i've edited my answer. thanksPithy
No problem, @Karim.Aleron
@Aleron 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.Pierce
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.Africah
@Aleron Normal code is executed in a blocking manner, not "non-blocking"Orchitis
@Pithy does this mean an async function without await isn blocking instead non-blocking? because synchronous is synonym of blocking, isn't?Suggest
hi @Orchitis +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?Paraformaldehyde
and when the call stack is empty, JavaScript resumes execution, right? @OrchitisParaformaldehyde
@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.Orchitis
S
76

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
Simonton answered 11/12, 2018 at 18:50 Comment(5)
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.Jeffjeffcoat
await can only be used inside async function.Simonton
Please Note this is old implementation before ES11. In ES11 things have changed little bit. But this will still work.Simonton
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?Hitoshi
The last example should have a promise in main(), and it won't be synchronous. So removing await doesn't necessarily make execution synchronous.Accomplishment
W
40

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.

Wallow answered 9/8, 2017 at 15:26 Comment(1)
Useful answer - for a longer explanation perhaps also try this blog - medium.com/javascript-in-plain-english/…Account
S
21

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);
Spokeswoman answered 9/4, 2020 at 11:27 Comment(6)
@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.Spokeswoman
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)Gaby
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?Mellissamellitz
@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.Spokeswoman
I dont get why the "4 (from then)" line gets printed after the 3. Why is this the case?Madel
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).Spokeswoman

© 2022 - 2024 — McMap. All rights reserved.