Exception handling in Async methods

Scenario 1: Take the following method for example:

public async void AsyncTaskWithAPossibleException()
{     
    //code that generates exception
}
public void MethodCallingAsyncTaskMethod()
{
    try
    {
        //Exceptions thrown by the AsyncTaskWithAPossibleException cannot be caught 
        //using the try catch block. It might crash the running process in most 
        //scenarios. 
        AsyncTaskWithAPossibleException();
    }
    catch (Exception)
    {
        // exception won't be caught
    }
}

Scenario 2.a: We can change the code in Scenario 1 to move the exception handling inside the AsyncTaskWithAPossibleException method.

public async void AsyncTaskWithAPossibleException()
{
    try
    {
        //code that generates exception
    }
    catch(Exception)
    {
        // Handle exception
    }
}
public void MethodCallingAsyncTaskMethod()
{
    AsyncTaskWithAPossibleException();
}

Scenario 2.b.: : Instead of handling the exception in the AsyncTaskWithAPossibleException method, we can also change the return type from void to Task(For a Async method, it is recommended to return a Task instead of a void).
Exception raised in the AsyncTaskWithAPossibleException method will be saved in the returning Task instance. When we await the AsyncTaskWithAPossibleException method, the exception saved in the Task will get rethrown with its stack trace preserved.

public async Task AsyncTaskWithAPossibleException()
{
    //code that generates exception
}
public async Task MethodCallingAsyncTaskMethod()
{
    try
    {
        await AsyncTaskWithAPossibleException();
    }
    catch (Exception)
    {
        // exception is caught with stack trace preserved
    }
}

Scenario 3: If for some reason we don’t await the Task, we can define a continuation when the Task ends with the faulted state.

public async Task AsyncTaskWithAPossibleException()
{
    //code that generates exception
}
public void MethodCallingAsyncTaskMethod()
{
    var task = AsyncTaskWithAPossibleException();
    task.ContinueWith(t =>
    {
        //  handle t.Exception
    }, TaskContinuationOptions.OnlyOnFaulted);
}

Scenario 4: If for some reason we don’t await the Task and we do not define a continuation when the Task ends with the faulted state, the exception saved inside the instance will be raised eventually when the GC collects the object. You can catch that silent exception using the TaskScheduler event UnobservedTaskException.

Continue Reading