Waiting for async

Waiting for a async method should be done right

Back in 2012 the .NET Framework 4.5 introduced asynchronous programming with async and await. Nowadays I regularly see it being used, but I can see that it just isn’t understood well.

Let me first say that async and await seems extremely simple when you look at it at a glance, but it really is not that easy. There are many things about async and await, that I could write about. View the async/await series here.

Calling an async method from non-async method

When you want to call an “async” method from your non-async code, let’s say a console application, it would seem that you can simple do the following.

static void Main(string[] args)
{
    var result = SomeAsyncCode().Result;
}

private static async Task<string> SomeAsyncCode()
{
    await Task.Delay(10);
}

That seems about right! BUT, what happens the async method could throw an exception?

static void Main(string[] args)
{
    try
    {
        var result = SomeAsyncCode().Result;
    }
    catch (ArgumentException ex)
    {
        Console.WriteLine($"Caught ArgumentException: {ex.Message}");
    }
}

private static async Task<string> SomeAsyncCode()
{
    await Task.Delay(10);
    throw new ArgumentException("No argument given!");
}

And here your application will CRASH and BURN violently with an unhandled “AggregateException”.

The problem

As I stated before, async and await is not trivial. The syntactic sugar of async and wait is actualy a serious amount of sugar! It’s like realizing that a 500ml/16oz bottle of soda contains a whopping 53 grams of sugar.

What many people do not realize is, that the compiler generates a state machine to handle toe async code. The code could potentially result in multiple exceptions. Those exceptions are wrapped in an AggregateException.

Nobody did see the “AggregateException” coming and normally you wouldn’t either. When you are using “await”, the compiler handles the “AggregateException” for you and throws the first exception it sees. If you choose to block an async call with “Result” or “Wait()” instead of “await”, you must deal with the “AggregateException” yourself.

How should you call an async method synchronously?

Instead of calling “Result”, you should call “GetAwaiter().GetResult()”.

static void Main(string[] args)
{
    try
    {
        var result = SomeAsyncCode().GetAwaiter().GetResult();
    }
    catch (ArgumentException ex)
    {
        Console.WriteLine($"Caught ArgumentException: {ex.Message}");
    }
}

This way your code works as expected and the “ArgumentException” will be caught. Not as intuitive as it should be. The syntactic sugar of async and await, just is not as trivial as the construction seems to be.

Wrap up

Never force a wait on an async method, but when you must use “GetAwaiter().GetResult()”.

I think async and await is really great. It makes writing asynchronous code relatively easy. The construction improves the readability of code and the code becomes easy to follow, because the compiler hides all complicated stuff from you. But don’t forget, asynchronous code is not simple and you should really understand what async and await actually do.

Check out the complete async/await series here.

Leave a Comment