Ok so that might be a bit overdramatic but now that I have your attention, please continue… :)
I love the asynchronous programming model that comes with .NET 4.5 and the async/await keywords in C#5 in since it alleviates the pain of…well…asynchronous programming by allowing for an imperative control flow in the code instead of a inversion-of-control-callback-galore style of code. However, the linear style that it promotes can be deceptive if you don’t remind your self from time to time of how it operates under the hood. I recently ran into the following issue…
At our company we’re in the process of building Windows Store applications which means we’re using the WinRT API. WinRT is an asynchronous API, most methods are asynchronous and don’t have any synchronous versions. This is a good thing, Microsoft wants us to build responsive apps (lets hope they practice what they preach on this matter… yes I’m looking at you Visual Studio team ;)).
One of the things our app has to provide is the ability to take pictures and the handler for doing this looks something like so:
Which does: tell WinRT to capture image, store image stream to file, save meta data associated with image (guid, path to image, user provided data) in data structure, store meta data to file. The code has two asynchronous calls, one for capturing the image and one for storing the image file but since we’re using the await keyword the code has a nice linear style instead of nested callbacks.
Now, lets do an innocent refactor for this code so that the concerns of storing and saving files and meta data for the image is done by another object:
Now AcquireFromCamera is much shorter and we’ve separated out the logic for handling the image captured event. We’ve also created a buggy piece of software. Take a look at the factored out ImageCaptured handler. The asynchronous method for creating the file, CreateFileAsync, is awaited and as such it will immediately return control to the calling method namely AcquireFromCamera. This behavior hasn’t changed but was has changed is the caller and the continuation i.e. the code that will be executed after the awaited method in the current method scope. In the refactored code, the last line in AcquireFromCamera - Frame.Navigate - will quite possibly be executed before the continuation of CreateFileAsync. This could not happen in the original code since the call to Frame.Navigate was a part of the continuation of CreateFileAsync. In the refactored version the code invoked by Frame.Navigate was in fact executed before the continuation of CreateFileAsync. Since the latter code mutates a state consumed by the former, needless to say, disaster occurred.
Note that the same mistake would have been less likely when using callbacks as continuations since these are explicitly scoped and the refactoring would require moving out code from the callback method to the outer scope.
While the async and await keywords lets you write code that has a seemingly linear flow, after the compiler has done its magic it’s nothing of the sort. What you should always remember is this:
Bottom line, be careful of how you structure and restructure your code when using async/await.