I have a question about when its safe to handle an aggregate exception when using WhenAll(). It seems like the natural place would be inside the catch block, because if the catch block never fires, it would imply there are no exceptions to handle. But I see a lot of code that has an empty catch block and checks for the existence of an AggregateException before handling any found exceptions (including on MS website).
public async Task MyMethod() {
var tasks = new List<Task>();
for (var i = 0; i < 10; i++) {
tasks.Add(DoSthAsync());
}
var masterTask = Task.WhenAll(tasks);
try {
var results = await masterTask;
} catch {
// Safe to access masterTask here and handle aggregate exceptions? Have all tasks completed?
foreach (var ex in masterTask.Exception.innerExceptions) {
HandleException(ex);
}
}
// Or necessary to check for and handle aggregate exceptions here?
if (masterTask.Exception != null) {
foreach (var ex in masterTask.Exception.innerExceptions) {
HandleException(ex);
}
}
}
public async Task DoSthAsync() {
// ...
}
The code that you posted works because
Task.WhenAllreturns a task that only completes when all sub tasks have completed.Why does the code do it that way? Why no
catch (Exception ex)? This is because await only throws the first inner exception. If you need access to multiple exceptions this code pattern is a good way to do it. You also could docatch (AggregateException ex)and use that object. It's the same object.I personally avoid using
catchlike that. Essentially it's using exceptions for control flow. This makes debugging harder and can lead to verbose code.I like this:
I made the
.ContinueWith(_ => { })bit into aWhenCompletedextension method so that the code looks clean.Then you wondered whether the second way to check for exceptions is a good idea:
You can certainly do that. Essentially it's the same thing. Use what is more convenient in the particular situation.