By far my favorite feature of .NET 4.5 is
await, or, as Microsoft call it, the Task-based Asynchronous Pattern (TAP). It is something I didn’t know I was missing until I saw a recording of Anders Hejlsberg’s Build talk on the subject. Shortly after this, I had a highly asynchronous C++ embedded project that lasted for over a year where I felt miserable building state machine after state machine to handle the inherent problem of all asynchronous programs: What do you do after your asynchronous operation completes?
This blog series is aimed at C# programmers new to
await. A general understanding of the
Task class introduced with .NET 4.0 is expected. In this first part, I’ll explain the
What is async?
async keyword can be used to decorate a method or lambda expression.
It is important to note that
async is not a part of the method signature, so if you are implementing an interface or overriding a virtual or abstract method you have a choice of whether to use
async or not.
async method may only return
Task<T> for any concrete type T.
void as a return type should be avoided where possible and is generally only needed in event handlers, making the method a fire-and-forget method where the caller has no way of knowing when the call finished or failed.
If you write a method returning
Task<T> you should generally follow the convention of using the suffix
Async in your method name to indicate that the method can be awaited (regardless of whether you method’s implementation uses
async or not).
Task should be returned for methods that would, if they were synchronous methods, return
Task<T> should be returned for methods that would otherwise return some type
T (i.e., anything not
void). Think of the
Task as the object the caller can use to keep track of what ever happened to that asynchronous method he triggered.
What effect does async have?
async will do two things to your method or lambda:
- It will allow you to write
awaitin your method (see my next blog post in this series).
- If your return type is not
voidthe compiler will automagically convert your
returnstatement (or the missing
returnstatement at the end of your method) into a
For a method that does not contain any
await calls, this means a completed
Task will be returned without the need to explicitly specify this. For the above example this means it will behave exactly like this non-
async version of it:
For a method that does contain an
await in the executed path, the method will return a
Task object that will turn to the
IsCompleted state when the last awaited call has completed and the synchronous code following it (if any) has finished executing. (For more on this, read my next blog post in this series about the
Do I need async?
Methods containing only one
await statement as the very last instruction in the method may generally be written without the
async keyword. For example, the method
is equivalent to
Even though this yields the same result, the method declared as
async seems to be the more readable version and has a slight runtime penalty. The other difference here is that if
stream.FlushAsync() throws an exception and
await is not used
FlushTheStreamAsync() will not appear in the exception’s call stack (more on exceptions in the next blog post).
How does that help me?
As mentioned earlier, the returned
Task object can be used to examine the state of the asynchronous call (still running? completed? failed? cancelled?). While this is possible using the various methods of the
Task class, the easiest way to handle this is through the
await keyword handled in the next blog post.
One reply on “async/await part 1: Understanding the async keyword”
Methods containing only one await statement as the very last instruction in the method may generally be written without the async keyword