Whats New in C# 5.0 Part-2
With the proposed new features, the asynchronous version of the code will instead look like this:
public async Task<int> SumPageSizesAsync(IList<Uri> uris) { int total = 0; foreach (var uri in uris) { statusText.Text = string.Format("Found {0} bytes ...", total); var data = await new WebClient().DownloadDataAsync(uri); total += data.Length; } statusText.Text = string.Format("Found {0} bytes total", total); return total; } Public Async Function SumPageSizesAsync(uris As IList(Of Uri)) As Task(Of Integer) Dim total As Integer = 0 For Each uri In uris statusText.Text = String.Format("Found {0} bytes ...", total) Dim data = Await New WebClient().DownloadDataAsync(uri) total += data.Length Next statusText.Text = String.Format("Found {0} bytes total", total) Return total End Function
Notice first how similar it is to the synchronous code. The highlighted parts are added, the rest stays the same. The control flow is completely unaltered, and there are no callbacks in sight. That doesn’t mean that there are no callbacks, but the compiler takes care of creating and signing them up, as we shall see.
The method is asynchronous by virtue of returning a Task<int> instead of an int. The Task and Task<T> types are in the framework today, and are good at representing ongoing work. The caller of SumPageSizesAsync can later use the returned Task<int> to inquire whether the work is complete, wait for the int result synchronously or sign up callbacks to get it when it is ready. So instead of taking a callback as a parameter, an asynchronous method now returns a representation of the ongoing work.
The asynchronous method has no extra parameters. By convention and to distinguish it from its synchronous counterpart (if we keep that in our code) we append “Async” to the method name.
The method is also marked as async. This means that the method body is compiled specially, allowing parts of it to be turned into callbacks, and automatically creating the Task<int> that is returned.
Here’s how that works: Inside of the method body we call another asynchronous method, DownloadDataAsync.
This quickly returns a Task<byte[]> that will eventually complete when the downloaded data is available.
However, we don’t want to do anything else until we have that data, so we immediately await the task, something that is only allowed inside of async methods.
At first glance the await keyword looks like it blocks the thread until the task is complete and the data is available, but it doesn’t. Instead it signs up the rest of the method as a callback on the task, and immediately returns. When the awaited task eventually completes, it will invoke that callback and thus resume the execution of the method right where it left off!
By the time execution reaches the return statement, we have already been suspended and resumed several times by await’ing in the foreach loop, and have returned to the original caller long ago. We returned them not a result, because we didn’t have one yet (we were still in the process of computing the total) but a Task<int> that they could await if and when they wanted to. The effect of the return statement is to complete that task, so that whoever is looking at it – e.g. by await’ing it – can now get the result.