The await operator in C# automatically unwraps faulted tasks and rethrows their exceptions. You can await the completion of multiple tasks by using Task.WhenAll, and then if any of those tasks are faulted all of their exceptions will aggregated and rethrown in a single exception by the await.
However, Task.WhenAny does not work the same way as Task.WhenAll. Task.WhenAny returns an awaitable Task<Task> where the child Task represents whichever Task completed first. A key difference being that the container task will not throw when awaited!
If an exception occurs inside of a Task.WhenAny, you can automatically rethrow the exception by doing (and I realize how weird this sounds) await await Task.WhenAny
Sample Tests
[Fact]
public async Task AwaitWhenAnyWait()
{
var t1 = Task.Run(async () =>
{
await Task.Delay(100);
throw new InvalidOperationException();
});
// This await will NOT throw.
var whenAny = await Task.WhenAny(t1);
Assert.True(whenAny.IsFaulted);
Assert.NotNull(whenAny.Exception);
Assert.IsType<InvalidOperationException>(whenAny.Exception.InnerException);
}
[Fact]
public async Task AwaitWhenAll()
{
var t1 = Task.Run(async () =>
{
await Task.Delay(100);
throw new InvalidOperationException();
});
try
{
// This await WILL throw.
await Task.WhenAll(t1);
throw new AssertException();
}
catch (InvalidOperationException)
{
}
}
[Fact]
public async Task AwaitAwaitWhenAny()
{
var t1 = Task.Run(async () =>
{
await Task.Delay(100);
throw new InvalidOperationException();
});
try
{
// This await await WILL throw.
await await Task.WhenAny(t1);
throw new AssertException();
}
catch (InvalidOperationException)
{
}
}
Enjoy,
Tom
This comment has been removed by the author.
ReplyDeleteCan I say await "Task(T)" and await await "Task(Task(T))" both throws if any exception occurs?
ReplyDelete