Part 28: Understanding Async and Awaitable Tasks
- Posted: Jun 25, 2013 at 5:21 PM
- 34,610 Views
- 2 Comments
Loading User Information from Channel 9
Something went wrong getting user information from Channel 9
Loading User Information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
Right click “Save as…”
In this lesson, I want to talk about the keywords we've added to several spots in our source code. The keywords I'm referring to are:
You see all three of them in the GetFlickrImages() method:
These keywords are newly added to C# 5.0 and are generally referred to as the new "async"—short for "asynchronous" or "asynchrony"—feature. In a nut shell, this new feature is a simplified way of improving the performance of the app and making it more responsive to the user without the complexity of writing code to use multiple threads. If you call a method of the Windows Phone API or some other library supporting async that could potentially take a "long time", the method will say, "I promise to get those results to you as soon as possible, so go on about your business and I'll let you know when I'm done". The app can then continue executing, and can even exit out of method contexts. Another word for this in computer programming terminology is a "promise".
Under the hood, when you compile this source code, the Compiler picks apart the source code and implements a complex series of statements in the Intermediate Language to allow this to happen flawlessly and it does it without the use of multiple threads in most cases.
Async is best used for operations that have a high degree of latency, but are not compute intensive. So, for example, we want to retrieve data from a Web API like we're doing in the GetFlickrImages() method. Before async, our app would block the execution of code waiting for Flickr's web service to reply with the data we requested. That could take a second, or two, or three, which is an eternity in computing. But the Phone's PROCESSOR is not busy at all. It's just sitting there, waiting for a reply from the Flickr web service. This is known as "I/O Bound" ... I/O means "In / Out" ... so things like the file system, the network, the camera, etc., involve "I/O bound operations" and are good candidates for async. In fact, the Windows Phone API designers decided to bake async into all I/O Bound operations, forcing you to use this to keep the phone responsive to the end user.
Contrast that to a compute-intensive operation such as a photo filter app that must take a large image from the camera and run complex mathematical algorithms to change the colors or the position of each pixel in the image. That could take a long time, too, but in that case, the Phone's processor is hard at work. This type of operation is known as "CPU Bound". This is NOT a good use of async. In this case, you would want to consider a Background Worker which helps to manage threads on the Windows Phone platform. If you are developing in .NET, you may prefer to work with threading via the Task Parallel Library instead, or revert back to managing threads manually, which has been an option since the very earliest versions of .NET.
Understanding multi-threading, parallel programming, the Task Parallel Library, even Background Workers on the Windows Phone API are WAY beyond the scope of this series and this lesson. It's a large topic and I'll not lie to you—it makes my head spin. If you want to learn a little more about async and how it applies to the Windows RunTime, check out:
Working with Async Methods in the Windows Runtime
... and be sure to read the comments where I further explain this idea.
But back to the topic at hand ... async is for those common occasions when you have a blocking operation that is not compute intensive, such as our case here where we are waiting for the HttpClient.GetStringAsync() operation to complete. In this case, we use the await keyword, which says, "I'll get back to you when I'm finished". The thread of execution can continue on through the other lines of code until it absolutely must have the results of that operation. Now, in our case, we attempt to call JsonConvert.DeserializeObject<FlickrData> in the very next line of code, so little advantage is gained. However, we must still use the await keyword because the HttpClient.GetStringAsync() method returns a Task<T>. That method requires we await. You'll see await-able methods with a common convention ... they all end with the suffix Async.
Furthermore, any method that uses an await-able method must be marked with the async keyword in their method signature, AND if it is supposed to return a value it must return the value wrapped with a Task<T>. If the method is void, it will merely be marked void in the method signature. Since the HttpClient.GetStringAsync() is marked with await, and our method returns a List<FlickrImage> we must mark our method with async and wrap the List<FlickrImage> with a Task<T>. That essentially means that any method that calls our method can continue executing until it absolutely needs that List<FlickrImage>.
Therefore, take a look at the method that calls our FlickrImage.GetFlickrImages() method:
That's why in line 37 (above) we must use the await keyword—our FlickrImage.GetFlickrImages() was forced to be await-able (since it used the HttpClient.GetStringAsync()). Notice that we also had to modify the event handler method's method signature adding the async keyword. Since this is what they refer to as a "top level method", the chain of adding async stops here.
Again, remind me, why are we doing all of this? In hopes of making our app more responsive. This operation should not be blocking the Phone's processor from taking on other tasks like answering a phone call or running background tasks on behalf of other apps. It won't prevent the user from selecting any of the hardware buttons on the phone and getting an instant response.
To recap, the big take away in this lesson is what async is, how it works, the scenarios it was designed to address, and it's overall purpose—to keep the phone and your apps responsive during long running I/O bound operations.