Await vs GetAwaiter

06 Dec 2018
Jorge Gueorguiev Garcia

Jorge Gueorguiev Garcia

See author's bio and posts

While I take my time to put the effort on my Safety Musings part 2 post, I have decided to talk about a small little thing regarding the retrieval of results on C# while using async.

This came to mind as I was looking at someone add Wait() to get the result of the async method. It triggered some memory about it, and I decided to play a bit to confirm what I remembered (my memory ain't great).

So lets have some small piece of code, that does nothing of interest, but throws an exception within the async call.

 class Program
    {
        static void Main(string[] args)
        {
            var thing = new TheThing();
            try
            {
                thing.CallingAsync().Wait();
                //thing.CallingAsync().GetAwaiter().GetResult();
            }
            catch (ArgumentException e)
            {
                Console.WriteLine("This is an argument exception");
                Console.WriteLine($"Message: {e.Message}");
                Console.WriteLine($"Stack: {e.StackTrace}");
            }
            catch (AggregateException e)
            {
                Console.WriteLine("This is an aggregate exception");
                Console.WriteLine($"Message: {e.Message}");
                Console.WriteLine($"Stack: {e.StackTrace}");
            }

            Console.WriteLine("Press any key");
            Console.ReadKey();
        }
    }

    public class TheThing
    {
        public async Task CallingAsync()
        {
            for (int i = 0; i < 4; i++ ) {
                Receiving(i);
                Console.WriteLine("This is Calling {0}", i);
            }
        }

        public void Receiving(int number)
        {
            Console.WriteLine("This is Receiving {0}", number);
            if (number == 2)
            {
                throw new ArgumentException("Hey, this is an exception");
            }
        }
    }

If we run it like it is, with thing.CallingAsync().Wait(); the results are as follow:

This is an aggregate exception
Message: One or more errors occurred. (Hey, this is an exception)
Stack:    
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at AwaitForMe.Program.Main(String[] args) in C:\Users\akira\code\tests\AwaitForMe\AwaitForMe\Program.cs:line 14

If we comment the line mentioned and uncomment thing.CallingAsync().GetAwaiter().GetResult() the results change:

This is an argument exception
Message: Hey, this is an exception
Stack:   
   at AwaitForMe.TheThing.Receiving(Int32 number) in C:\Users\akira\code\tests\AwaitForMe\AwaitForMe\Program.cs:line 52
   at AwaitForMe.TheThing.CallingAsync() in C:\Users\akira\code\tests\AwaitForMe\AwaitForMe\Program.cs:line 40
   at AwaitForMe.Program.Main(String[] args) in C:\Users\akira\code\tests\AwaitForMe\AwaitForMe\Program.cs:line 15

So Wait() collects exceptions into an AggregateException, while GetAwaiter().GetResult() returns the exception thrown. The problem with the AggregateException is that the type of Exception and the Stack Trace become hidden within the InnerException. So, for example, if you need to log the exception that is being thrown, unless the logger knows how to unroll the exception, Wait() gives you back information that is not very useful. Which one should you use will then depend on the rest of the system that you are using.

Oh, additional bit, if you wonder what happens with Result on a task that returns a value, the issue is the same as with Wait().