Async await - does await block other code from running? - javascript

In javascript, does await block code? For example, let's say we have the below code:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Will "selectUser" wait to run until addUser is finished so that we can select the user that is added?
Also, let's say that we add some code between the awaits that is not a promise, something like this:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
setTimeout(() => console.log('Do something that takes 3 seconds'), 3000);
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Will "selectUser" wait for addUser but not the setTimeout? If so, how would you write the above code to make addUser run first, then setTimeout and then selectUser?
I would also like to add that I have been studying and reading on both stackoverflow and other resources, but I need some clarification.

Will "selectUser" wait to run until addUser is finished so that we can
select the user that is added?
From MDN Docs - await:
The await expression causes async function execution to pause until a
Promise is settled (that is, fulfilled or rejected), and to resume
execution of the async function after fulfillment. When resumed, the
value of the await expression is that of the fulfilled Promise.
Execution of queryDB function will be paused while it waits for the first promise to settle. Second line of code will only execute after first one has completed successfully.
Will "selectUser" wait for addUser but not the setTimeout?
Yes, that is correct.
If so, how would you write the above code to make addUser run first, then
setTimeout and then selectUser?
You could wrap setTimeout in a function that returns a promise and then await that promise to make sure last line executes after first two have completed.
Following is an example of a wrapper function that wraps setTimeout
function waitForTimeout(seconds) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Hello World");
resolve();
}, seconds * 1000);
});
}
Once you have a wrapper function, you can await it as shown below:
async function queryDB() {
const addUser = await promisePool.execute(
"INSERT INTO Users (User) VALUES ('username')"
);
await waitForTimeout(3); // wait for 3 seconds
const selectUser = await promisePool.execute("SELECT User FROM Users")
}

Important note: blocks is probably a bad word here. A better word is it waits. Even better would be probably to just say that operations that are done using await syntax are guaranteed to be executed sequentially.
The Big Picture
The main reason for using asynchronous code in Node is that we don't block the whole app while we wait for some asynchronous operation like a database request, a network request, a file operation etc. - we only block this particular execution context.
Even though Node.js only uses one thread for executing user code, but I/O operations are asynchronous and they're non-blocking.
So imagine you have an endpoint with the code that you presented above, that is linked to some button "Add user" on the front end.
Bill presses the button, you start handling the request, start waiting for addUser operation
At this point John also presses same button. Your code will still be executed and will also start to wait until addUser finishes.
Let's say that Users table has gotten into a deadlock and any db operations with it will be super slow, and we have a third user, Jane, who is just browsing your site but she doesn't need to sign in/sign up, so she doesn't touch Users table. What happens? Jane is totally fine, her requests will go through without any issues.
How to await setTimeout
setTimeout is coming from "previous era" so it uses callback instead of async/await syntax. But it is always possible to convert callback style to async/await, if you need it.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await delay(5000);

In short, await only blocks the code(s) that's after the await statement in the current async function, not the whole thread. Once the await has been resolved the rest of the code gets executed. In your example, since you have 2 await statements, the second one won't run until the first one has finished, because the first one blocks the function that both await statements are inside of. For the second example, since setTimeout() doesn't return a promise and you aren't awaiting it either, the function will treat it as a 'synchronous' operation. That is, it will throw the setTimeout() callback into the call stack till the next event loop and continue executing the next line which is another await statement that will block the function again until it is resolved.
In order to execute all three lines one by one, you'll have to put the setTimeout() into a promise and await it so that JS treats it as an asynchronous operation and 'waits' for it to finish before moving on to the next line.

Will "selectUser" wait to run until addUser is finished so that we can select the user that is added?
Yes.
Will "selectUser" wait for addUser but not the setTimeout? If so, how would you write the above code to make addUser run first, then setTimeout and then selectUser?
"selectUser" will be executed after "addUser", but not the setTimeout callback. If you want to run "selectUser" after setTimeout, you can add it inside the setTimeout callback function. So it should look like:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
setTimeout(async () => {
const selectUser = await promisePool.execute("SELECT User FROM Users");
}, 3000);
}

Short answer is yes, it blocks. The purpose of await is, to make sure you have the result available in the next line. So if you needed the value of addUser to make the next query, you would have it.
It was introduced so you don't have to write then() at the end of the first line and put the second call into a callback, which makes the code hard to read.

Related

Asynchronous code executing sequentially after wait instead of showing console output immediately

Here is my sample code:
async function counter() {
console.log('1');
let next = new Promise((resolve) => {setTimeout(() => resolve(3), 5000)});
let three = await next;
console.log('2');
console.log(three)
}
counter();
The accepted answer on this question says that
When you execute something synchronously, you wait for it to finish
before moving on to another task. When you execute something
asynchronously, you can move on to another task before it finishes.
What I understand from this is that 2 should be logged to the console immediately since the function is asynchronous and it shouldn't wait for another task (the promise) to finish (resolve).
However, what I see is that both 2 and 3 are logged together in the console after a wait of 5 seconds. Isn't that what a synchronous function would do based on the above text from the accepted answer?
Thanks.
The await expression causes async function execution to pause until a Promise is settled (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise.
You can read more about Await Here
And you can read more about this acccepted answer here
What happens basically is if you used await inside your async function, it causes to wait for it until promise is resolved. but outside of that function will still run in parallel to this function.

What's the best way to run a promise synchronously when it's already resolved?

When a promise is resolved, it resumes code execution after any awaits in the next task.
So for instance:
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
}
foo();
console.log("end");
prints "start", "end", "foo". Even though the promise is already resolved, it still waits for any other code to execute first, and only after that it resumes and prints foo.
I would like to execute any awaits synchronously for resolved promises.
The only way I can think of to achieve this is to check if a promise has been fulfilled and wrap the await in an if statement so that it only waits if the promise is still pending. But figuring out if a promise is pending (and getting the resolved value) seems very tedious to do synchronously. Does anyone know if there's a better way?
More context
I have a game loop that needs to not run for more than a few milliseconds per frame. There is one specific function that I need to call in order to prepare some gltf assets. This function takes more than a second. So I'd like to devide this up into chunks, so it excecutes only part of this function every frame.
The easiest way to achieve this that I could think of was to make the function async and checking if a certain amount of time has passed. If more than a few milliseconds have passed, I will await waitForFrameRender(), which will essentially stop the execution until the next frame. However, this will add tons of awaits in the function (it has a bunch of loops), and most of them won't really have to wait for anything because the function hasn't been running for more than X amount of milliseconds yet.
Therefore it seemed to make more sense to skip these waits and run these parts synchronously.
In my current setup I have an if statement to check how much time has passed, and only await if it has actually been running for more than a few milliseconds. Which works to a certain degree, but the function that takes a second to execute also has nested functions that are async for the same reason. So the nested function calls still need an await.
I realize that this sounds like web workers are the solution. I haven't actually tried this yet but I feel like transferring the javascript object that the function returns will have too much overhead for this. And since it is ok for this function to not return immediately (even 20 seconds or so would be fine) simply making the function async seemed like the easiest way to do it.
If this is what you want move end into the promise. The other alternative would be to put end in its own promise
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
console.log("end");
}
foo();
or
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
}
foo();
await Promise.resolve();
console.log("end");
This is probably what you want. You need to await until foo() is done running.
async function run() {
console.log("start");
await foo();
console.log("end");
}
run();

Await several async functions, but continue when one finishes

I'm trying to write a function that searches using 2 algorithms. The main function should call both main algorithm functions simultaneously, but continue as soon as one finishes. It should also stop the other function from running.
I currently have an entry function set up like this:
async function entry(code) {
let [ product_algorithm_1, product_algorithm_2 ] = await Promise.all([
get_info_algorithm_1(code),
get_info_algorithm_2(code)
])
// Here I would check which variable is not empty, and display the results
}
This works fine, but the issue is that it waits for both functions to finish before continuing. I'm trying to continue when one finishes, and deleting the others process. Does anyone know how to achieve this?
What you're looking for is Promise.race(). It will return when one of the calls has resolved. Find out more on MDN.
async function entry(code) {
let product_algorithm_fastest = await Promise.race([
get_info_algorithm_1(code),
get_info_algorithm_2(code)
])
}
Its worth noting that the returned value will be that of the promise that was "fastest"

Firing Vuex actions asynchronously and sequentially - what am I not understanding?

I have a Vuex store and I am trying to fetch data from the Firebase Realtime Database. I am initially fetching the user information, however afterwards I would like to fetch some other information that relies upon the initial data fetched.
As you can see from the code, I am trying to do this using async / await, however whenever firing the two actions in my created() hook, the user's information isn't initialised, and therefore the second action fails.
My user store
async fetchCreds({ commit }) {
try {
firebase.auth().onAuthStateChanged(async function(user) {
const { uid } = user
const userDoc = await users.doc(uid).get()
return commit('SET_USER', userDoc.data())
})
} catch (error) {
console.log(error)
commit('SET_USER', {})
}
}
My club action which relies upon the above call
async fetchClubInformation({ commit, rootState }) {
try {
const clubIDForLoggedInUser = rootState.user.clubId
const clubDoc = await clubs.doc(clubIDForLoggedInUser).get()
return commit('SET_CLUB_INFO', clubDoc.data())
} catch (error) {
console.log(error)
}
}
}
The methods being called within my component's created() method.
created: async function() {
await this.fetchCreds();
await this.fetchClubInformation();
this.loading = false;
}
I have a feeling I'm fundamentally misunderstanding async / await, but I can't understand what in the code is incorrect - any help or advice would be greatly appreciated.
I'm not particularly familiar with Firebase but after a bit of digging through the source code I think I can shed a little light on your problems.
Firstly, consider the following example:
async function myFn (obj) {
obj.method(function () {
console.log('here 1')
})
console.log('here 2')
}
await myFn(x)
console.log('here 3')
Question: What order will you see the log messages?
Well here 2 will definitely come before here 3 but it's impossible to tell from the code above when here 1 will show up. It depends on what obj.method does with the function it's been passed. It might never call it at all. It might call it synchronously (e.g. Array's forEach method), in which case here 1 will appear before the other messages. If it's asynchronous (e.g. timers, server calls) then here 1 may not show up for some time, long after here 3.
The async modifier will implicitly return a Promise from the function if it doesn't return a Promise itself. The resolved value of that Promise will be the value returned from the function and the Promise will resolve at the point the function returns. For a function without a return at the end that's equivalent to it finishing with return undefined.
So, to stress the key point, the Promise returned by an async function will only wait until that function returns.
The method onAuthStateChanged calls its callback asynchronously, so the code in that callback won't run until after the surrounding function has completed. There's nothing to tell the implicitly returned Promise to wait for that callback to be invoked. The await inside the callback is irrelevant as that function hasn't even been called yet.
Firebase makes extensive use of Promises, so typically the solution would just be to return or await the relevant Promise:
// Note: This WON'T work, explanation follows
return firebase.auth().onAuthStateChanged(async function(user) {
// Note: This WON'T work, explanation follows
await firebase.auth().onAuthStateChanged(async function(user) {
This won't work here because onAuthStateChanged doesn't actually return a Promise, it returns an unsubscribe function.
You could, of course, create a new Promise yourself and 'fix' it that way. However, creating new Promises using new Promise is generally considered a code smell. Typically it's only necessary when wrapping code that doesn't support Promises properly. If we're working with a library that has proper Promise support (as we are here) then we shouldn't need to create any Promises.
So why doesn't onAuthStateChanged return a Promise?
Because it's a way of watching all sign-in/sign-out events. Every time the user signs in or signs out it'll call the callback. It isn't intended as a way to watch a particular sign-in. A Promise can only be resolved once, to a single value. So while a single sign-in event could be modelled with a Promise it's meaningless when watching all sign-in/sign-out events.
So fetchCreds is registering to be notified about all sign-in/sign-out events. It doesn't do anything with the returned unsubscribe function, so presumably it'll be listening to all such events until the page is reloaded. If you call fetchCreds multiple times it'll keep adding more and more listeners.
If you're waiting for a user to finish signing in then I suggest waiting for that directly instead. firebase.auth() has various methods starting with the prefix signIn, e.g. signInWithEmailAndPassword, and these do return a Promise that resolves when the user has finished signing in. The resolved value provides access to various information, including the user. I don't know which method you're using but the idea is much the same for all of them.
However, it might be that you're really just interested in grabbing the details of the current user. If that's all you want then you don't need to use onAuthStateChanged at all. You should just be able to grab a copy using the currentUser property. Something like this:
async fetchCreds({ commit }) {
try {
const { uid } = firebase.auth().currentUser
const userDoc = await users.doc(uid).get()
commit('SET_USER', userDoc.data())
} catch (error) {
console.log(error)
commit('SET_USER', {})
}
}
As I've already mentioned, this relies on the assumption that the user is already signed in. If that isn't a safe assumption then you might want to consider waiting until after sign in has completed before creating components that need user credentials.
Update:
Questions from the comments:
If the obj.method() call was asynchronous and we did await the callback function within it, would that ensure that the outer async function (myFn) never resolves before the inner one has finished?
I'm not entirely sure what you're asking here.
Just to be clear, I'm being very careful with my use of the words async and asynchronous. A function such as setTimeout would be considered asynchronous but it is not async.
async/await is just a lot of syntactic sugar around Promises. You don't really wait for a function, you wait for a Promise. When we talk about awaiting an async function we're really talking about waiting for the Promise it returns to resolve.
So when you say await the callback function it's not really clear what that means. Which Promise are you trying to await?
Putting the async modifier on a function doesn't make it magically wait for things. It will only wait when it encounters await. You can still have other asynchronous calls within an async function and, just like with a normal function, these calls will be performed after the function has returned. The only way to 'pause' is to await a Promise.
Putting an await inside another function, even a nested function, won't make any difference to whether the outer function waits unless the outer function is already waiting for the inner function. Behind the scenes this is all just Promises chaining then calls. Whenever you write await you're just adding another then call to a Promise. However, that won't have the desired effect unless that Promise is in the same chain as the Promise returned by the outer async function. It only needs one link to be missing for the chain to fail.
So modifying my earlier example:
async function myFn (obj) {
await obj.method(async function () {
await somePromise
// ...
})
// ...
}
await myFn(x)
Note that there are 3 functions here: myFn, method and the callback passed to method. The question is, will await myFn(x) wait for somePromise?
From the code above we can't actually tell. It would depend on what method does internally. For example, if method looked like this then it still wouldn't work:
function method (callback) {
setTimeout(callback, 1000)
}
Putting async on method won't help, that'll just make it return a Promise but the Promise still won't be waiting for the timer to fire.
Our Promise chain has a broken link. myFn and the callback are both creating their parts of the chain but unless method links those Promises together it won't work.
On the other hand, if method is written to return a suitable Promise that waits for the callback to complete then we will get our target behaviour:
function method (callback) {
return someServerCallThatReturnsAPromise().then(callback)
}
We could have used async/await here instead but there was no need as we can just return the Promise directly.
Also, if in the async myFn function you're not returning anything, does that mean it'll resolve immediately and as undefined?
The term immediately is not well-defined here.
If a function isn't returning anything at the end then it's equivalent to having return undefined at the end.
The Promise returned by an async function will resolve at the point the function returns.
The resolved value for the Promise will be the value returned.
So if you aren't returning anything it will resolve to undefined. Resolving won't happen until the end of the function is reached. If the function doesn't contain any await calls then this will happen 'immediately' in the same sense as a synchronous function returning 'immediately'.
However, await is just syntactic sugar around a then call, and then calls are always asynchronous. So while the Promise might resolve 'immediately' the await still has to wait. It's a very short wait, but it isn't synchronous and other code may get the opportunity to run in the meantime.
Consider the following:
const myFn = async function () {
console.log('here 3')
}
console.log('here 1')
Promise.resolve('hi').then(() => {
console.log('here 4')
})
console.log('here 2')
await myFn()
console.log('here 5')
The log messages will appear in the order they're numbered. So even though myFn resolves 'immediately' you'll still get here 4 jumping in between here 3 and here 5.
To make it short
fetchCreds({ commit }) {
return new Promise((resolve, reject) => {
try {
firebase.auth().onAuthStateChanged(async function(user) {
const { uid } = user
const userDoc = await users.doc(uid).get()
commit('SET_USER', userDoc.data())
resolve()
})
} catch (error) {
console.log(error)
commit('SET_USER', {})
resolve()
}}
}
async () => undefined // returns Promise<undefined> -> undefined resolves immediatly
asnyc () => func(cb) // returns Promise<any> resolves before callback got called
() => new Promise(resolve => func(() => resolve())) // resolves after callback got called

Is javascript async await good for web applications

While reading up on some async/await examples with the new javascript syntax, I found this code example:
const axios = require('axios'); // promised based requests - like fetch()
function getCoffee() {
return new Promise(resolve => {
setTimeout(() => resolve('☕'), 2000); // it takes 2 seconds to make coffee
});
}
async function go() {
try {
// but first, coffee
const coffee = await getCoffee();
console.log(coffee); // ☕
// then we grab some data over an Ajax request
const wes = await axios('https://api.github.com/users/wesbos');
console.log(wes.data); // mediocre code
// many requests should be concurrent - don't slow things down!
// fire off three requests and save their promises
const wordPromise = axios('http://www.setgetgo.com/randomword/get.php');
const userPromise = axios('https://randomuser.me/api/');
const namePromise = axios('https://uinames.com/api/');
// await all three promises to come back and destructure the result into their own variables
const [word, user, name] = await Promise.all([wordPromise, userPromise, namePromise]);
console.log(word.data, user.data, name.data); // cool, {...}, {....}
} catch (e) {
console.error(e); // 💩
}
}
go();
One thing that is not clear (in the linked example the whole script waits for the the coffee function to return), it appears to be that async/await is a blocking action? If this is the case does this not mean it is a bad idea for a node web application?
I have just been thrown into a project using AWS DynomoDB whose abstraction class for all actions is behind async/await... if this is blocking, this will surely destroy performance?
await does block! ..
await indeed makes your code wait.
There is no magic that will let the code that comes after the await run while you're still waiting. In the example, those ajax requests are not going to be fired until the coffee is done.
..but it can wait for multiple asynchronous jobs..
However, you can await multiple asynchronous processes. The example that you post at some point fires three simultaneous Ajax requests and waits for those only after they have all been fired. Those requests will be executed in parallel.
..you only have to wait when you need answers
The point is that you start something asynchronously, which will eventually end (promise!). You can call await when (and preferably only when) you really need to wait on the result of that call. So if you call every async method using await, then the effect is gone, but if you call them, store the promise in a variable, and then await them at the end, then you're running your long actions in parallel. The example shows the "bad" use (getting coffee and not getting to work until it's done), and then the good use (doing three requests at the same time).
You could 'optimize' the example by only awaiting the coffee and the first request at the end, just like it waits for the other requests, so it does 5 things in parallel instead of 3. This only works when you don't need the result of the first async function in the next.
.. and await blocks only you, not your caller
I said before that calling await right away for a single synchronous function is bad. This is not always the case, because you can only use await in a function that is asynchronous itself, so the caller of your function isn't blocked (unless it also calls await).
So for example you could make a simple function for fetching some data, that wraps a more complicated Ajax call using await AjaxShizzleGoesHere(). Even though your function just calls one other thing and awaits it, it is still possible to run it in parallel. Your function has to wait for the Ajax call to return (so it uses await, and is blocked), but the caller of your function doesn't have to await the result right away, and it can start other asynchronous functions first.

Categories

Resources