Categories
Programming

async/await part 1: Understanding the async keyword

By far my favorite feature of .NET 4.5 is async/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 async/await. A general understanding of the Task class introduced with .NET 4.0 is expected. In this first part, I’ll explain the async in async/await.

What is async?

The 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.

Return types

An async method may only return void, Task or 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 or 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 void. 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?

Writing async will do two things to your method or lambda:

  1. It will allow you to write await in your method (see my next blog post in this series).
  2. If your return type is not void the compiler will automagically convert your return statement (or the missing return statement at the end of your method) into a Task<T> or Task.

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 await keyword.)

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”

Leave a Reply

Your email address will not be published. Required fields are marked *