Best practices

Avoid async void

  • The only place where you can safely use async void is in event handlers. Consider the following code:

    private async Task<bool> SomeFuncAsync() {
      ...
      await ...
    }
    public void button1_Click(object sender,  EventArgs e) {
      var result = SomeFuncAsync().Result;
      SomeOtherFunc();
    }
    

    Once the async call completes, it waits for the SynchronizationContext to become available. However, the event handler holds on to the SynchronizationContext while it is waiting for SomeFuncAsync method to complete; thus causing a circular wait (deadlock).

    To fix this we need to modify the event handler to:

    public async void button1_Click(object sender,  EventArgs e) {
      var result = await SomeFuncAsync();
      SomeOtherFunc();
    }
    
  • Any exception thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.

    private async void SomeFuncAsync() {
      throw new InvalidOperationException();
    }
    public void SomeOtherFunc() {
      try {
        SomeFuncAsync();
      }
      catch (Exception ex) {
        Console.WriteLine(ex);
        throw;
      }
    }
    

    the exception is never caught by the catch block in SomeOtherFunc.

  • async void methods don’t provide an easy way to notify the calling code that they’ve completed

  • async void methods are difficult to test. MSTest asynchronous testing support only works for async methods returning Task or Task<T>.