Await on a completed task same as task.Result?
Solution 1:
There are already some good answers/comments here, but just to chime in...
There are two reasons why I prefer await
over Result
(or Wait
). The first is that the error handling is different; await
does not wrap the exception in an AggregateException
. Ideally, asynchronous code should never have to deal with AggregateException
at all, unless it specifically wants to.
The second reason is a little more subtle. As I describe on my blog (and in the book), Result
/Wait
can cause deadlocks, and can cause even more subtle deadlocks when used in an async
method. So, when I'm reading through code and I see a Result
or Wait
, that's an immediate warning flag. The Result
/Wait
is only correct if you're absolutely sure that the task is already completed. Not only is this hard to see at a glance (in real-world code), but it's also more brittle to code changes.
That's not to say that Result
/Wait
should never be used. I follow these guidelines in my own code:
- Asynchronous code in an application can only use
await
. - Asynchronous utility code (in a library) can occasionally use
Result
/Wait
if the code really calls for it. Such usage should probably have comments. -
Parallel task code can use
Result
andWait
.
Note that (1) is by far the common case, hence my tendency to use await
everywhere and treat the other cases as exceptions to the general rule.
Solution 2:
This makes sense if timeoutTask
is a product of Task.Delay
, which I believe what it is in the book.
Task.WhenAny
returns Task<Task>
, where the inner task is one of those you passed as arguments. It could be re-written like this:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
In either case, because downloadTask
has already completed, there's a very minor difference between return await downloadTask
and return downloadTask.Result
. It's in that the latter will throw AggregateException
which wraps any original exception, as pointed out by @KirillShlenskiy in the comments. The former would just re-throw the original exception.
In either case, wherever you handle exceptions, you should check for AggregateException
and its inner exceptions anyway, to get to the cause of the error.