How and when to use ‘async’ and ‘await’
When using async
and await
the compiler generates a state machine in the background.
Here's an example on which I hope I can explain some of the high-level details that are going on:
public async Task MyMethodAsync()
{
Task<int> longRunningTask = LongRunningOperationAsync();
// independent work which doesn't need the result of LongRunningOperationAsync can be done here
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation
{
await Task.Delay(1000); // 1 second delay
return 1;
}
OK, so what happens here:
Task<int> longRunningTask = LongRunningOperationAsync();
starts executingLongRunningOperation
Independent work is done on let's assume the Main Thread (Thread ID = 1) then
await longRunningTask
is reached.Now, if the
longRunningTask
hasn't finished and it is still running,MyMethodAsync()
will return to its calling method, thus the main thread doesn't get blocked. When thelongRunningTask
is done then a thread from the ThreadPool (can be any thread) will return toMyMethodAsync()
in its previous context and continue execution (in this case printing the result to the console).
A second case would be that the longRunningTask
has already finished its execution and the result is available. When reaching the await longRunningTask
we already have the result so the code will continue executing on the very same thread. (in this case printing result to console). Of course this is not the case for the above example, where there's a Task.Delay(1000)
involved.
How should we use async await?
Every-time you call await
it creates a lump of code to bundle up variables, captures the synchronization context (if applicable) and create a continuation into an IAsyncStateMachine
.
Essentially, returning a Task
without the async
keyword will give you a small run-time efficiency and save you a bunch of CIL. Do note that the Async feature in .NET also has many optimizations already. Also note (and importantly) that returning a Task
in a using
statement will likely throw an Already Disposed Exception.
You can compare the CIL and plumbing differences here
- Forwarded Task
- Async Method
So if your method is just forwarding a Task
and not wanting anything from it, you could easily just drop the async
keyword and return the Task
directly.
More-so, there are times when we do more than just forwarding and there is branching involved. This is where, Task.FromResult
and Task.CompletedTask
come into play to help deal with the logic of what may arise in a method. I.e If you want to give a result (there and then), or return a Task
that is completed (respectively).
Lastly, the Async and Await Pattern has subtle differences when dealing with Exceptions. If you are returning a Task
, you can use Task.FromException<T>
to pop any exception on the the returned Task
like an async
method would normally do.
Nonsensical example
public Task<int> DoSomethingAsync(int someValue)
{
try
{
if (someValue == 1)
return Task.FromResult(3); // Return a completed task
return MyAsyncMethod(); // Return a task
}
catch (Exception e)
{
return Task.FromException<int>(e); // Place exception on the task
}
}
In short, if you don't quite understand what is going on, just await
it; the overhead will be minimal. However, if you understand the subtitles of how to return a task result, a completed task, placing an exception on a task, or just forwarding. You can save your self some CIL and give your code a small performance gain by dropping the async
keyword returning a task directly and bypassing the IAsyncStateMachine
.
At about this time, I would look up the Stack Overflow user and author Stephen Cleary, and Mr. Parallel Stephen Toub. They have a plethora of blogs and books dedicated solely to the Async and Await Pattern, all the pitfalls, coding etiquette and lots more information you will surely find interesting.
At the end of an async method, should I return or await?
You can't return the task if the method itself is declared to be async
- so this won't work, for example:
async Task BarAsync()
{
return BazAsync(); // Invalid!
}
That would require a return type of Task<Task>
.
If your method is just doing a small amount of work and then calling just one async method, then your first option is fine, and means there's one fewer task involved. You should be aware that any exceptions thrown within your synchronous method will be delivered synchronously though - indeed, this is how I prefer to handle argument validation.
It's also a common pattern for implementing overloading e.g. by cancellation token.
Just be aware that if you need to change to await something else, you'll need to make it an async method instead. For example:
// Version 1:
Task BarAsync()
{
// No need to gronkle yet...
return BazAsync();
}
// Oops, for version 2 I need to do some more work...
async Task BarAsync()
{
int gronkle = await GronkleAsync();
// Do something with gronkle
// Now we have to await BazAsync as we're now in an async method
await BazAsync();
}
Where is the return statement in async/await
The async
keyword transforms the method and constructs the returned Task
instance. There is nothing returned from the async void
method because it returns void
; this lack of a Task
is one reason why you should avoid async void
. async void
is not a natural asynchronous method signature; it is only supported so that event handlers may be async
.
If you want to return a value, then you should have the method return a Task<T>
, e.g., Task<int> BlahAsync()
, and then you can just return the value directly, e.g., return 13;
The number of await
s in the method has nothing to do with it. When the method executes the actual return (e.g., return 13
), the async
keyword interprets that as completing the Task<int>
that was already constructed.
I have an async
intro on my blog that you may find helpful.
Is it useful to mark a method async when it only awaits at the return statement?
doesn't this mean that method is basically synchronous
No. It's asynchronous. You're probably thinking of sequential (progressing from one thing to the next), not synchronous (blocking the current thread). An await
will pause the method (sequentially) but not block the thread (asynchronously). For more information, see my async
intro.
without the async modifier
While you could elide the async
/await
keywords, I would recommend that you do not. This is because // some more code here
may throw an exception. I cover this and other considerations in my blog post on eliding async
and await
.
and the suffix Async?
No, that suffix is appropriate for any method that returns an awaitable (e.g., Task
). So, even if you elide the async
and await
, it's still returning a task that should be awaited, so it should still have the Async
suffix.
You can think of it this way: the Async
suffix is part of the API interface. The async
keyword is an implementation detail. They often go together, but not always.
Why does this async function expect a return value
You return a bool
value while your method actually returns a Task<bool>
.
Change the method to return the awaited Task
.
return await Task.Run(...);
async Task then await Task vs Task then return task
It is almost the same (in terms of threads etc.). But for the second one (using await
) a lot more overhead will be created by the compiler.
Methods declared as async
and using await
are converted into a state machine by the compiler. So when you hit the await
, the control flow is returned to the calling method and execution of your async
method is resumed after the await
when the awaited Task
has finished.
As there is no more code after your await
, there is no need to use await
anyway. Simply return the Task
is enough.
Related Topics
Using C# Regular Expressions to Remove HTML Tags
How to Check If a File Is in Use
Convert a String to an Enum in C#
Retrieving Property Name from Lambda Expression
Creating a Byte Array from a Stream
How to Match an Entire String With a Regex
Could Not Load File or Assembly or One of Its Dependencies
Multiple Levels in MVC Custom Routing
How to Query an Ntp Server Using C#
How to Dynamically Generate HTML Code Using .Net'S Webbrowser or Mshtml.Htmldocument
How to Convert a Unix Timestamp to Datetime and Vice Versa
How to Use Linq to Select Object With Minimum or Maximum Property Value
How to Pass an Array into a SQL Server Stored Procedure
Public Fields Versus Automatic Properties
In C#, Difference Between Public, Private, Protected, and Having No Access Modifier