using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace PCL.Core.Utils.Exts; public static class TaskExtensions { /// /// 返回第一个成功完成的 Task 的结果。 /// 如果所有 Task 都失败或被取消,则抛出 AggregateException。 /// /// Task 的返回类型 /// 要等待的任务集合 /// 第一个成功完成的 Task 的结果 /// 任务集合为空 /// 所有任务均未成功完成 public static async Task> WhenAnySuccessAsync(this IEnumerable> tasks) { ArgumentNullException.ThrowIfNull(tasks, nameof(tasks)); var taskList = tasks.ToList(); if (taskList.Count == 0) throw new ArgumentException("Task collection is empty.", nameof(tasks)); var remaining = new HashSet>(taskList); while (remaining.Count > 0) { var completed = await Task.WhenAny(remaining); remaining.Remove(completed); if (completed.IsCompletedSuccessfully) { return completed; } } // 所有任务都失败或被取消 var faultedExceptions = taskList .Where(t => t.IsFaulted) .SelectMany(t => t.Exception?.Flatten().InnerExceptions ?? Enumerable.Empty()) .ToList(); if (faultedExceptions.Count > 0) { throw new AggregateException("All connection attempts failed.", faultedExceptions); } // 如果没有失败任务,但还有任务,为 canceled if (taskList.Any(t => t.IsCanceled)) { throw new OperationCanceledException("All connection attempts were canceled."); } // Task 要么成功,要么 Faulted,要么 Canceled,大概率走不到这 throw new InvalidOperationException("All tasks completed but none succeeded, failed, or canceled."); } extension(Task task) { /// /// 不关心 Task 后续的异常,但是需要观察下 /// public void Forget() { _ = task.ContinueWith(t => _ = t.Exception, TaskContinuationOptions.OnlyOnFaulted); } } }