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
?
Task.WhenAny
doesn't rethrow exceptions from the individual tasks (unlikeTask.WhenAll
):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:Or in your case: