Async Wandering Part 5 — Catching exceptions from async void

This is the fifth part of the Async Wandering series. For your convenience you can find other parts in the table of contents in Part 1 – Why creating Form from WinForms in unit tests breaks async?

If you are interested in the topic see the talk page

Last time we saw how to wait for async void methods. Today we are going to catch exceptions. Normally exceptions thrown by those methods crash application because we cannot wait for them. Exceptions from async Task on the other hand don’t do that generally (they can when the GC cleans up and unobserved exception is being thrown) but silently wait for us to be handled. We would like to have exactly the same behavior, so let’s see, how we can do that.

Last time we implemented custom synchronization context. Today we need something more — task scheduler:

We have collection of tasks to execute. Whenever we queue new task, we just store it in the array instead of trying to execute it right away. Effectively those tasks are not run at all until we say so.

Now let’s see the refined context:

We create new TaskFactory with our scheduler so whatever we run on this factory will go through our logic. Next, we count operations so we know when we are done.

Finally, we have a helper method for running the tasks. We replace synchronization context with ours, submit a task with lambda provided by the caller, and then execute all tasks from the scheduler one by one. We wait for all the tasks so whatever exception is thrown we just “catch” it with ordinary Task mechanisms.

Finally, let’s try it:

We schedule a lambda to run, add some sleeping and finally wait for the task with ordinary trycatch logic. The exception is propagated and handled correctly.

Bonus chatter: what is the difference between Wait() and GetAwaiter().GetResult()? Which one should we actually use in our library?