Please, observe the following trivial code:
class Program
{
static void Main()
{
var sw = new Stopwatch();
sw.Start();
try
{
Task.WhenAny(RunAsync()).GetAwaiter().GetResult();
}
catch (TimeoutException)
{
Console.WriteLine("Timed out");
}
Console.WriteLine("Elapsed: " + sw.Elapsed);
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}
private static async Task RunAsync()
{
await Observable.StartAsync(async ct =>
{
for (int i = 0; i < 10; ++i)
{
await Task.Delay(500, ct);
Console.WriteLine("Inside " + i);
}
return Unit.Default;
}).Timeout(TimeSpan.FromMilliseconds(1000));
}
}
Running it outputs:
Inside 0
Inside 1
Elapsed: 00:00:01.1723818
Press Enter to exit
Note, no Timed out message.
Now, if I replace Task.WhenAny
with Task.WhenAll
here is what I get:
Inside 0
Inside 1
Timed out
Elapsed: 00:00:01.1362188
Press Enter to exit
Note the presence of the Timed out message this time.
And, if I remove the Task.WhenAll
wrapper at all and call RunAsync
directly:
Inside 0
Inside 1
Timed out
Elapsed: 00:00:01.1267617
Press Enter to exit
The Timed out message is there, as expected.
So what is the deal with Task.WhenAny
? It obviously interrupts the asynchronous method, but where is the TimeoutException
?
As others have noted, Task. WhenAll only aggregates the tasks; it does not start them for you.
Exceptions are propagated when you use one of the static or instance Task. Wait methods, and you handle them by enclosing the call in a try / catch statement. If a task is the parent of attached child tasks, or if you are waiting on multiple tasks, multiple exceptions could be thrown.
Task.WhenAny
doesn't rethrow exceptions from the individual tasks (unlike Task.WhenAll
):
"The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the
RanToCompletion
state with itsResult
set to the first task to complete. This is true even if the first task to complete ended in theCanceled
orFaulted
state."
From Task.WhenAny
That means that it will complete successfully no matter what without any type of exceptions.
To actually rethrow the exception of the individual completed task you need to await
the returned task itself:
var completedTask = await Task.WhenAny(tasks); // no exception
await completedTask; // possible exception
Or in your case:
Task.WhenAny(RunAsync()).GetAwaiter().GetResult().GetAwaiter().GetResult();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With