Skip to main content

Exception handling and async void methods

·2 mins

TL;DR #

async void methods should be avoided:

  1. They do not allow error propagation outside of them.
  2. They cannot be awaited unlike async Task, which might cause concurrency issues.
  3. Use try/catch inside the async void or replace it with an awaited async Task method.

Issue #

Say you have a method definition that expects an Action delegate.

Task ReceiveMessageAsync(WebSocket socket, Action<WebSocketReceiveResult, byte[]> handleMessage);

Action delegates are used to pass event handlers that have no return type to other methods.

Then you try and handle the error coming from the Action (async void method) as such:

try
{
    await _manager.ReceiveMessageAsync(_webSocket, HandleMessage);
}
catch (ConnectionAbortedException ex)
{

}

The exception does not get handled and crashes the application. async void methods are not designed to do this. However, async Task methods are.

Solutions #

Pass a Func delegate to a function. However now you have to make sure it’s also awaited inside the method.

Method signature changes to this:

public Task ReceiveMessageAsync(WebSocket socket, Task<WebSocketReceiveResult, byte[], Task> handleMessage)
{
    // Receive message
    await handleMessage(result, buf);
}

Of course, it’s not ideal inside a method that receives messages. This could cause some lag between messages, as it would be handling a message and delaying other messages.

If it’s really neccesary wrap the code in the method with a try/catch block:

private async void HandleMessage(WebSocketReceiveResult result, byte[] buf)
{
    try
    {
        // Handle message
    }
    catch (ConnectionAbortedException ex)
    {

    }
}

Hope this helps, as it caused me some pain understanding why the error was not getting propagated. 😄


Read More:

Async/Await - Best Practices in Asynchronous Programming