The Async CTP "Why Do the Keywords Work THAT Way" Unofficial FAQ• Comments
There’s a lot of interest in the Async CTP, with good reason. The Async CTP will make asynchronous programming much, much easier than it has ever been. It’s somewhat less powerful but much easier to learn than Rx.
The Async CTP introduces two new keywords, async and await. Asynchronous methods (or lambda expressions) must return void, Task, or Task<TResult>.
This post is not an introduction to the Async CTP; there’s plenty of tutorial resources available out there (including one by yours truly). This post is an attempt to bring together the answers to a few common questions that programmers have when they start using the Async CTP.
Inferring the Return Type
When returning a value from an async method, the method body returns the value directly, but the method itself is declared as returning a Task<TResult>. There is a bit of “disconnect” when you declare a method returning one type and have to return another type:
Question: Why can’t I write this:
Consider: How will the method signature look to callers? Async methods that return a value must have an actual result type of Task<TResult>. So GetValue will show up in IntelliSense as returning Task<TResult> (this would also be true for the object browser, Reflector, etc).
Inferring the return type was considered during the initial design, but the team concluded that the keeping the “disconnect” within the async method was better than spreading the “disconnect” throughout the code base. The “disconnect” is still there, but it’s smaller than it could be. The consensus is that a consistent method signature is preferred.
Consider: There is a difference between async void and async Task.
An async Task method is just like any other asynchronous operation, only without a return value. An async void method acts as a “top-level” asynchronous operation. An async Task method may be composed into other async methods using await. An async void method may be used as an event handler. An async void method also has another important property: in an ASP.NET context, it informs the web server that the page is not completed until it returns (see my MSDN article for more information on how this works).
Inferring the return type would remove the distinction between async void and async Task; either all async methods would be async void (preventing composability), or they would all be async Task (preventing them from being event handlers, and requiring an alternative solution for ASP.NET support).
There is still a “disconnect” between the method declaration return type and the method body return type. Another option that has been suggested is to add a keyword to return to indicate that the value given to return is not really what’s being returned, e.g.:
Consider: Converting large amounts of code from synchronous to asynchronous.
The async return keyword was also considered, but it wasn’t compelling enough. This is particularly true when converting a lot of synchronous code to asynchronous code (which will be common over the next few years); forcing people to add async to every return statement just seemed like “needless busy-work.” It’s easier to get used to the “disconnect”.
The async keyword must be applied to a method that makes use of await. However, it also gives a warning if it is applied to a method that does not make use of await.
Question: Why can’t async be inferred based on the presence of await:
Consider: Backwards compatibility and code readability.
Eric Lippert has the definitive post on the subject. It’s also been discussed in [blog comments]https://docs.microsoft.com/en-us/archive/blogs/ericlippert/asynchronous-programming-in-c-5-0-part-two-whence-await?WT.mc_id=DT-MVP-5000058), Channel9, forums, and Stack Overflow.
To summarize, a single-word await keyword would be too big of a breaking change. The choice was between a multi-word await (e.g., await for) or a keyword on the method (async) that would enable the await keyword just within that method. Explicitly marking methods async is easier for both humans and computers to parse, so they decided to go with the async/await pair.
Question: Since it makes sense to explicitly include async (see above), why can’t await be inferred based on the presence of async:
Consider: Parallel composition of asynchronous operations.
At first glance, inferring await appears to simplify basic asynchronous operations. As long as all waiting is done in serial (i.e., one operation is awaited, then another, and then another), this works fine. However, it falls apart when one considers parallel composition.
Parallel composition in the Async CTP is done using TaskEx.WhenAny and TaskEx.WhenAll methods. Here’s a simple example which starts two operations immediately and asynchronously waits for both of them to complete:
In order to do parallel composition, we must have the ability to say we’re not going to await an expression.