A Tour of Task, Part 6: Results

The task members discussed in this blog post are concerned with retrieving results from the task. Once the task completes, the consuming code must retrieve the results of the task. Even if the task has no result, it’s important for the consuming code to examine the task for errors so it knows whether the task completed successfully or failed.

Result

The Result member only exists on the Task<T> type; it does not exist on the Task type (which represents a task without a result value).

T Result { get; }

Like Wait, Result will synchronously block the calling thread until the task completes. This is generally not a good idea for the same reason it wasn’t a good idea for Wait: it’s easy to cause deadlocks.

Furthermore, Result will wrap any task exceptions inside an AggregateException. This usually just complicates the error handling.

Exception

Speaking of exceptions, there’s a member specifically just for retrieving the exceptions from a task:

AggregateException Exception { get; }

Unlike Result and Wait, Exception will not block until the task completes; if called while the task is still in progress, it will just return null. If the task completes successfully or is cancelled, then Exception will still return null. If the task is faulted, then Exception will return the task’s exceptions wrapped in an AggregateException. Again, this usually just serves to complicate the error handling.

GetAwaiter().GetResult()

The GetAwaiter member was added to Task and Task<T> in .NET 4.5, and it’s available as an extension method on .NET 4.0 using the Microsoft.Bcl.Async NuGet package. Normally, the GetAwaiter method is just used by await, but it is possible to call it yourself:

Task<T> task = ...;
T result = task.GetAwaiter().GetResult();

The code above will synchronously block until the task completes. As such, it is subject to the same old deadlock problems as Wait and Result. However, it will not wrap the task exceptions in an AggregateException.

The code above will retrieve the result value from a Task<T>. The same code pattern can also be applied to Task (without a result value); in this case “GetResult” actually means “check the task for errors”:

Task task = ...;
task.GetAwaiter().GetResult();

In general, I try my best to avoid synchronously blocking on an asynchronous task. However, there are a handful of situations where I do violate that guideline. In those rare conditions, my preferred method is GetAwaiter().GetResult() because it preserves the task exceptions instead of wrapping them in an AggregateException.

await

Of course, await is not a member of the task type; however, I feel it’s important to remind today’s readers that the best way of retrieving results from a Promise Task is to merely use await. await retrieves task results in the most benign manner possible: await will asynchronously wait (not block); await will return the result (if any) for a successful task; and await will (re-)throw exceptions for a failed task without wrapping them in an AggregateException.

In short, await should be your go-to option for retrieving task results. The vast majority of the time, await should be used instead of Wait, Result, Exception, or GetAwaiter().GetResult().