Когда задача не выполняется, мы не можем получить доступ к ее свойству Result
, потому что она выбрасывает. Таким образом, чтобы получить результаты частично успешного задания WhenAll
, мы должны убедиться, что задание будет успешно выполнено. Тогда возникает проблема, что делать с исключениями неудавшихся внутренних задач. Глотать их, вероятно, не очень хорошая идея. По крайней мере, мы хотели бы войти в них. Вот реализация альтернативы WhenAll
, которая никогда не генерирует, но возвращает и результаты, и исключения в структуре ValueTuple
.
public static Task<(T[] Results, Exception[] Exceptions)> WhenAllEx<T>(params Task<T>[] tasks)
{
return Task.WhenAll(tasks).ContinueWith(_ => // return a continuation of WhenAll
{
var results = tasks
.Where(t => t.Status == TaskStatus.RanToCompletion)
.Select(t => t.Result)
.ToArray();
var aggregateExceptions = tasks
.Where(t => t.IsFaulted)
.Select(t => t.Exception) // The Exception is of type AggregateException
.ToArray();
var exceptions = new AggregateException(aggregateExceptions).Flatten()
.InnerExceptions.ToArray(); // Trick to flatten the hierarchy of AggregateExceptions
return (results, exceptions);
}, TaskContinuationOptions.ExecuteSynchronously);
}
Пример использования:
var tPass1 = Task.FromResult(1);
var tFail1 = Task.FromException<int>(new ArgumentException("fail1"));
var tFail2 = Task.FromException<int>(new ArgumentException("fail2"));
var task = WhenAllEx(tPass1, tFail1, tFail2);
task.Wait();
Console.WriteLine($"Status: {task.Status}");
Console.WriteLine($"Results: {String.Join(", ", task.Result.Results)}");
Console.WriteLine($"Exceptions: {String.Join(", ", task.Result.Exceptions.Select(ex => ex.Message))}");
Выход:
Статус: RanToCompletion
Результаты: 1
Исключения: сбой1, сбой2