Async await - does await block other code from running?

Viewed 5170

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.

5 Answers

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