As I’ve had occasion to look at more C#/XAML code lately, I’ve noticed a common pattern around the await keyword. Make no mistake: await is a marvelous language construct that makes it super-simple to write code using async APIs that otherwise looks like synchronous code:

private async Task<[type]> MyFunction()
{
String result1 = await Operation1();
String result2 = await Operation2(result1);
processResults(result2);
await Operation3();
return [something];
}

Once EcmaScript6 is adopted in the app host for Windows Store apps written in JavaScript, we’ll see the same thing in that language as well.

What’s important to remember about await, however, is that it’s not always necessary. That is, if you simply need to start an async operation but don’t need its results right away, then you don’t need to stick an await in front of it and block the current thread. That is, you don’t want to force synchronous behavior where it’s not actually necessary, allowing instead for some operations to run in parallel.

In the code snippet above, for instance, we clearly need to wait for Operation1 to finish before invoking Operation2 because we need result1 as input to Operation2. Operation3, however, is not dependent on Operation2 at all, so it can clearly be running while we wait for Operation2 to finish.

In addition, because we’re not returning anything from Operation3, we don’t need to await that one before returning from the function.

Remember that await is just an easy way to avoid writing explicit async structures with an explicit completed handler (as you have to do in JavaScript today), and if you don’t need to do anything with those results, then it’s not necessary to use await at all. (For more background on async and await, see Diving deep with WinRT and await on the Win8 Developer Blog.)

The code above, then could be written as follows to allow Operation2 and Operation3 to run in parallel, along with anything that happens after MyFunction returns:

private async Task<[type]> MyFunction()
{
String result1 = await Operation1();

String asyncOp = Operation2(result1);
asyncOp.Completed = delegate(IAsyncOperation<String> result2, AsyncStatus status)
{
if (status == AsyncStatus.completed)
{
processResults(result2)
}
};

Operation3();
return [something];
}

It’s more code to write, but will generally make for better performance in your app.

 

 


2 Comments

  1. Posted October 7, 2013 at 7:35 am | Permalink

    Kraig,

    I would be very careful about not awaiting async operations as you have discussed. Don’t get me wrong, in some circumstances it can be beneficial, but you have to be 100% sure what you are doing. A few problems I can think of are,

    1. The big one in my opinion – if you run an async operation without await, what happens if the operation throws an exception? It does depend to some extent on the application host, but at best the exception is thrown in a global handler with loss of call-stack, timing, ability to recover, etc. At worse the exception is just lost.

    2. The async/await infrastructure does a lot of work to ensure that the correct thread is used when returning from an async operation. If your ‘processResults(…)’ method above was to do some updates to the UI, then you may find the callback is on a non-UI thread resulting in an exception. If you had used await then you would know that call is on the correct thread.

    3. A bit easier to handle if you know the code, but you are making the assumption that just because ‘Operation3()’ takes no arguments, that it doesn’t rely on any side-effects of the previous operations. If it does (or if the implementation changes in the future) then you have a race condition.

    Just a few warnings there. I would also check out the ‘Task.WhenAll(…)’ method that makes running operations in parallel a bit more safe.

    Andy

  2. Jasd
    Posted October 7, 2013 at 11:48 am | Permalink

    But what if Operation3 may throw an exception? It’d be swallowed, wouldn’t it? And what if a caller expects Operation3 to be completed after MyFunction returns?