Learning to do Async / Await – 4 mistakes I initially made

As part of work on updates for my Shoppers Calculator app, I stumbled into bugs caused by my not using Async await properly and not really understanding how it worked.

Some errors I was making:

  • Believed it was spawning new threads
  • Returned void from methods when there would have been no return when doing synchronous programming
  • Used goofy code patterns to make async work, thinking that was what you had to do.
  • Decided it was OK to use async methods without awaiting and call them synchronously from other methods (you get a warning but no error).

Shoppers Calculator 1.8 introduced a shopping history.  Previously there was a single shopping trip or single cart. If you wanted a new trip/empty cart you opened the app bar and clicked “clear everything”. Now we have a history, and to do this you click New Shopping Trip on the main screen.  your previous one is saved and you can view it later. So now, rather than saving a single collection of items as a trip, I am saving a collection of collections of items.

Not really a problem, and my app works just fine, but I could see quickly that I needed a follow up update that addresses the issue of performance by making my save method asynchronous.  Not likely to be a problem as folks only go shopping a few times most weeks, and even an avid shopper won’t have as much data as to cause a big issue yet.

So starting with the save method, I started adding async into method signatures and await into calls, using asynchronous overloads when I could and not worrying too much about it when I couldn’t. I did this all the way up the call stack to the UI though all the various ways save would be called, and moved on to other perceived poorly performing scenarios.

In the end, a large number of methods got the async keyword.  It didn’t work and I instantly had problems loading and saving data, despite my reading of the code to be fine. So I took to the web, and to channel 9 videos, and now it works. My app is performant and smooth.

So what have I learned?

  • The Async method doesn’t spin up a new thread.

Actually this is pretty key to understanding how it all works and why the other things can cause a problem.  It’s more of a deferred action, or a do-it when you get to it type thing.  Basically, what it’s really saying is as this method executes, there will be a moment that takes time and rather than wait for that next line to run, you should do something else, and return to it once that response has come in.

public async void ButtonClicked(object sender, EverntArgs e)
{
    Spinner.Visibility=Visibility.Visible;   
    await StartProcessing()
    Spinner.Visibility = Visibility.Collapsed;
}

This code would probably be suitable in either asynchronous or synchronous code. The reason is that the await keyword doesn’t mean that any new thread is being spun up – things still happen in order. If await was causing a new thread, the equivalent of calling Task.Run(()=>StartProcessing), the spinner would show for approximately 0 seconds. Using await still means the last line won’t execute until the one before it done, but also that while we are waiting, control is released back to the caller, in this case unblocking the UI so the user can interact with it. It doesn’t mean the processing will finish any faster, but it does mean the user can do other things while we await the completion of processing.

  • Don’t use async void (except for handlers)

The compiler is expecting async methods to return a task and hooks into the task as a callback. By not returning a task, there is no callback created. It’s fire and forget and the caller has no idea when the awaited method has returned.

Instead, use Task as the return type. This gives the caller something to hook into and check the status of.

Are there cases when async void is ok? Yes, event handlers, such as the button example above would be ok.

  • You should (mostly) just code like you would for a synchronous method

Due to my belief that it was spinning separate threads, I wrote code in a way that tried to maximize thread efficiency.  A big waste of time really, as all it did was make methods less readable. In most cases, just write the way you normally would. There is however, one gotcha. You have to remember that everything after the method you are awaiting, may not run immediately as control will be returned back up the stack. If there are things that need to happen immediately, call them first as long as they don’t depend on the result of the awaited method. You would probably just do this anyway if you were writing synchronous code, but perhaps in synchronous code you would do hold off on some actions while the thread is blocked due to processing something or getting data. These should be removed, or moved to occur before the awaited method if possible.

For example:

public void Add(double value)
{
   model.Add(value)
   model.Save();
   OnPropertyChanged("Total");
}

Could become:

public async Task Add(double value)
{
   model.Add(value)
   OnPropertyChanged("Total");
   await model.Save();
}

In this change, we are making sure the UI is updated quickly, before the potentially slow process of saving the model state.

Calling synchronous methods as if they were async doesn’t work

I mentioned at the start that I began my async journey with the save method which I perceived as an IO method and a natural fit for async.await. However, the place I was saving this single collection of collections didn’t have an async method to it. So I pretended it did and wrapped it in an async method and called it from an async method with an await etc, up the chain.

Ultimately, we are waiting for some task that takes some time to run. Sticking async Task in the method signature by itself doesn’t do too much. This is basically what I was doing at one point, using Windows Phone 8 Silverlight synchronous storage methods assuming that because it’s a storage method, some sort of real IO was going on and therefore I should do that.

I don’t remember now which bugs if any we caused by that specifically as I was also using async void and all the other failings mentioned above and they were more responsible for issues with my app. However, it’s fair to say that if you are not ultimately using framework methods that are async, but hitting on a long running process, something along the way should create a new task that executes the code and return that task from the method, or use something like TaskCompletionSource (see links below).  Being able to trust that async methods are really async is important as we have seen above – it can affect the expectations you have for them and the way you use them. It affects scalability and is generally a bad idea, as this post explains.

Clearing up these errors have helped me create a more performant application and fixed some nagging errors. It was frustrating going through the process of learning this while trying to launch a product update but it was valuable lesson and something I wouldn’t have learned without having a side project (I don’t get to use this stuff at work where we’re having to support Windows XP.

A couple of resources that really helped me with this:

Video: Six Essential Tips For Async on Channel9 by Lucian Wischik
Written: Asynchronous Programming with Async and Await on MSDN
Written: Should I expose asynchronous wrappers for synchronous methods? Steven Toub on MSDN
Written: TaskCompletionSource – Trying to understand threadless async work – Stack overflow (This links to several useful posts on the subject).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.