Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch exception thrown from an async lambda

I am trying to write a method that tries to execute an action but swallows any exceptions that are raised.

My first attempt is the following:

public static void SafeExecute(Action actionThatMayThrowException) {
    try {
        actionThatMayThrowException();
    } catch {
        // noop
    }
}

Which works when called with a synchronous action:

SafeExecute(() => { 
    throw new Exception(); 
});

However fails when called with an asynchronous action:

SafeExecute(async () => { 
    await Task.FromResult(0);
    throw new Exception(); 
});

Is is possible to write a method that handles both scenarios?

like image 264
kimsagro Avatar asked Aug 14 '14 09:08

kimsagro


People also ask

What happens if an exception is thrown within an asynchronous method?

As we know, in asynchronous programming, control does not wait for the function's result and it executes the next line. So when the function throws an exception, at that moment the program control is out of the try-catch block.

How do I get Async exception?

To catch an exception that an async task throws, place the await expression in a try block, and catch the exception in a catch block. Uncomment the throw new Exception line in the example to demonstrate exception handling. The task's IsFaulted property is set to True , the task's Exception.

Does await throw exception?

If we use the “await” keyword, the exception will be thrown: This solves the problem for a single simple task, but what if we have multiple tasks running within a method?


1 Answers

To correctly handle async delegates you shouldn't use Action (this will cause the lambda expression to be async void which is dangerous and should be avoided), you should use Func<Task> to be able to await it:

public static async Task SafeExecute(Func<Task> asyncActionThatMayThrowException)
{
    try
    {
        await asyncActionThatMayThrowException();
    }
    catch
    {
        // noop
    }
}

This will solve the async case, but not the synchronous case. You can't do both with a single method. To do that you would need a different method, but it can still call the async one to enable reuse:

private static readonly Task<object> _completedTask = Task.FromResult<object>(null);

public static void SafeExecute(Action actionThatMayThrowException)
{
    SafeExecute(() =>
    {
        actionThatMayThrowException();
        return _completedTask;
    });
}

I wouldn't actually recommend disregarding unhandled exceptions in this way. You should consider at least logging the exception.

like image 150
i3arnon Avatar answered Sep 20 '22 01:09

i3arnon