When is omitting await acceptable in asynchronous functions?

87 views Asked by At

As far as I understand it, the await keyword prevents a function from continuing until the called asynchronous function completes. Essentially, it makes the asynchronous function synchronous. So that the following holds:

await MyAsyncMethod();      // first this is called
await MyOtherAsyncMethod(); // only when the above completes, this is called 
MyNonAsyncMethod();         // similarly, this is only called when the above completes

Now in most code editors, omitting the await keyword results in a warning. This is baffling to me given my understanding of what async is supposed to accomplish. For if we await every asynchronous method, are we not thereby making them all synchronous, and thus defeating the very purpose of asynchronous programming?

For example, in my specific use-case, I have a function that saves a blogpost to a database, and emails all of the blog's subscribers to notify them that a new post has been created. From the perspective of the user that creates the blogpost, it is pointless to wait for the result of the mailing. The mailing logic is a simple for loop that just sends emails out to the subscribers and returns no information worth considering. The user only cares that the blogpost has been created, and that the mailing logic has initiated, and he wants to receive this information promptly. I have therefore left out the await keyword and have allowed the process to continue in the background, while returning to the user a value that indicates that all is OK.

public async Task<IActionResult> Create(Blogpost blogpost)
{
  try 
  {
     // save to db
     await CreateBlogPostAsync(blogpost);
     // initiate mailing logic
     NotifySubscribersAsync();

     return Ok(blogpost);
  }
  catch
  {
    // return error code
    return StatusCode(500);
  }
}

This works perfectly for my use-case. I don't want the user to have to wait a long time for the sending process to complete before he receives an OK signal. I thought that this is precisely what asynchronous programming was meant to accomplish: allowing processes to happen in the background while the function continues. Yet, though the code compiles and runs alright for my purposes, VSCode warns me that I should be using the await keyword.

What is it I am not understanding here?

2

There are 2 answers

6
Nox On

So what is the point of awaiting something. The main idea of asynchronous programming is to have a blocking task like IO run in the background, as to not block the main thread and keep your application specific.

Why does Visual Studio Code tell you to await your function call

Well, because most of the time you may want the task to run in the background, but the lines below your function call may depend on the function completing first.

That is the default assumption when your function returns a task.

If you wan't to just call your function and don't care about it's return change it's return type to void. If you do so Visual Studio Code should not bother you.

5
David On

From the perspective of the user that creates the blogpost, it is pointless to wait for the result of the mailing.

Omitting await is the wrong way to do that. The operations that the user is waiting for are:

  • Write to the DB
  • Queue the notifications

The first one can easily be async as data access libraries commonly support that. The latter depends on how you queue the notifications.

If you treat that notifications process as "fire and forget" then the problem is the "forget" part. What if it fails? Nothing is observing that result, so nothing will know it failed.

On the other hand, instead of sending the notifications immediately and ignoring the result, do something which simply queues the notifications and immediately returns control. This could be putting a message on a message queue somewhere, it could be writing a record to a database, etc. These can easily be async operations that finish quickly and return to the user.

Then a separate application would be monitoring that message queue, database table, etc. to observe the queued process, and would begin that logic. That application does want to wait for the result of the mailing, observing if each operations succeeds or fails.

Basically, the example you're using isn't really related to async. It's asynchronous in the sense that a separate process handles the heavy lifting while the user continues whatever they're doing. But it's not using async and await for that.


It's often good to intuitively separate "asynchronous" from async. The latter is a subset of the former, but lots of things are "asynchronous" that have nothing to do with async.

Asynchronous operations can vary significantly. Maybe they use async, maybe they use callbacks, maybe they use entirely separate processes/applications, etc., etc.

But async and await in the vast majority of cases are much simpler than that. They're mostly used when you have an otherwise semantically synchronous operation which under the hood relies on hardware that can perform the operation without the CPU waiting for it. Network access, disk access, DB access, etc.

async and await are the tools the code uses to indicate this, and to allow the application to still be responsive to input while waiting for other hardware to return information. Common examples include not blocking a UI thread in a desktop application so it can still respond to clicks while performing a heavy operation, or allowing a web server to re-use threads and continue to handle incoming requests while some users wait for heavy operations.