Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capturing Exceptions on async operations

I'm reading up more about async here: http://msdn.microsoft.com/en-us/library/hh873173(v=vs.110).aspx

Going through this example:

Task<bool> [] recommendations = …;
while(recommendations.Count > 0)
{ 
    Task<bool> recommendation = await Task.WhenAny(recommendations);    
    try
    {
        if (await recommendation) BuyStock(symbol);
        break;
    }
    catch(WebException exc)
    {
        recommendations.Remove(recommendation);
    }
}

I wonder, if I'm already performing await on Task.WhenAny why do I need to await again inside of the try block?

If I already did this: Task<bool> recommendation = await Task.WhenAny(recommendations); Why do this: if (await recommendation) BuyStock(symbol);

like image 825
amhed Avatar asked Sep 02 '14 13:09

amhed


People also ask

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.

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.

Does await throw exception?

When you await a Task, the first exception is re-thrown, so you can catch the specific exception type (such as InvalidOperationException). However, when you synchronously block on a Task using Task. Wait or Task. Result, all of the exceptions are wrapped in an AggregateException and thrown.


2 Answers

The first await exists to asynchronously wait for the first task to complete (i.e. recommendation). The second await is only there to extract the actual result out of the already completed task, and throw exceptions stored in the task. (it's important to remember that awaiting a completed task is optimized and will execute synchronously).

A different option to get the result would be using Task<T>.Result, however it differs in the way it handles exceptions. await would throw the actual exception (e.g WebException) while Task<T>.Result would throw an AggregateException containing the actual exception inside.

Task<bool> [] recommendations = …;
while(recommendations.Count > 0)
{ 
    Task<bool> recommendation = await Task.WhenAny(recommendations);    
    try
    {
        if (recommendation.Result) 
        {
            BuyStock(symbol);
        }
        break;
    }
    catch(AggregateException exc)
    {
        exc = exc.Flatten();
        if (exc.InnerExceptions[0] is WebException)
        {
            recommendations.Remove(recommendation);
        }
        else
        {
            throw;
        }
    }
}

Clearly awaiting the task is simpler and so it's the recommended way of retrieving a result out of a task.

like image 54
i3arnon Avatar answered Oct 25 '22 01:10

i3arnon


The use of await here creates the desired error handling semantics. If he used Result instead of await then the AggregateException would be rethrown directly; when using await the first exception within the AggregateException is pulled out an that exception is re-thrown. Clear the author of this code wanted the WebException to be thrown, rather than an AggregateException that he would need to manually unwrap.

Could he have used another approach, sure. This was simply the approach that the author of the code preferred, as it allows him to write the code more like traditional synchronous code rather than radically changing the style of the code.

like image 39
Servy Avatar answered Oct 24 '22 23:10

Servy