Я получил асинхронный метод, работающий как расширенный Task.WhenAll
. Он берет кучу задач и возвращается, когда все выполнено.
public async Task MyWhenAll(Task[] tasks) {
...
await Something();
...
// all tasks are completed
if (someTasksFailed)
throw ??
}
Мой вопрос заключается в том, как получить метод, возвращающий задачу, похожую на ту, которая была возвращена из Task.WhenAll
, если одна или несколько задач не были выполнены?
Если я соберу исключения и сгенерирую AggregateException
, они будут помещены в другое исключение AggregateException.
Редактировать: полный пример
async Task Main() {
try {
Task.WhenAll(Throw(1), Throw(2)).Wait();
}
catch (Exception ex) {
ex.Dump();
}
try {
MyWhenAll(Throw(1), Throw(2)).Wait();
}
catch (Exception ex) {
ex.Dump();
}
}
public async Task MyWhenAll(Task t1, Task t2) {
await Task.Delay(TimeSpan.FromMilliseconds(100));
try {
await Task.WhenAll(t1, t2);
}
catch {
throw new AggregateException(new[] { t1.Exception, t2.Exception });
}
}
public async Task Throw(int id) {
await Task.Delay(TimeSpan.FromMilliseconds(100));
throw new InvalidOperationException("Inner" + id);
}
Для Task.WhenAll
исключение составляет AggregateException
с двумя внутренними исключениями.
Для MyWhenAll
исключение составляет AggregateException
с одним внутренним AggregateException
с 2 внутренними исключениями.
Редактировать: Почему я это делаю
Мне часто нужно вызывать API пейджинга: я хочу ограничить количество одновременных подключений.
Фактические сигнатуры методов:
public static async Task<TResult[]> AsParallelAsync<TResult>(this IEnumerable<Task<TResult>> source, int maxParallel)
public static async Task<TResult[]> AsParallelUntilAsync<TResult>(this IEnumerable<Task<TResult>> source, int maxParallel, Func<Task<TResult>, bool> predicate)
Это означает, что я могу делать пейджинг вот так
var pagedRecords = await Enumerable.Range(1, int.MaxValue)
.Select(x => GetRecordsAsync(pageSize: 1000, pageNumber: x)
.AsParallelUntilAsync(maxParallel: 5, x => x.Result.Count < 1000);
var records = pagedRecords.SelectMany(x => x).ToList();
Все работает нормально, агрегат внутри агрегата - лишь незначительное неудобство.