using static System.Threading.Tasks.TaskContinuationOptions; using static System.Threading.Tasks.TaskStatus; namespace InnovEnergy.Lib.Utils; public static class TaskUtils { public static async Task TimeoutAfter(this Task task, TimeSpan timeout) { // We need to be able to cancel the "timeout" task, so create a token source var cts = new CancellationTokenSource(); var timeoutTask = Task.Delay(timeout, cts.Token); var completedTask = await Task .WhenAny(task, timeoutTask) .ConfigureAwait(false); if (completedTask != task) throw new TimeoutException($"Task timed out after {timeout}"); cts.Cancel(); // Cancel the "timeout" task so we don't leak a Timer return await task; // await the task to bubble up any errors etc } public static Task WhenAny(this IEnumerable tasks) { return Task.WhenAny(tasks).Unwrap(); } public static Task WhenAny(this IEnumerable> tasks) { return Task.WhenAny(tasks).Unwrap(); } public static Task WhenAll(this IEnumerable> tasks) { return Task.WhenAll(tasks); } public static Task WhenAll(this IEnumerable tasks) { return Task.WhenAll(tasks); } public static Task OnError(this Task task, Func> action) { return task .ContinueWith(t => action(t.Exception!), OnlyOnFaulted) .Unwrap(); } public static Task OnError(this Task task, Func onError) { return task.ContinueWith(t => onError(t.Exception!), OnlyOnFaulted); } public static Task OnError(this Task task, Func onError) { return task.ContinueWith(_ => onError(), OnlyOnFaulted); } public static Task OnError(this Task task, T onError) { return task.ContinueWith(_ => onError, OnlyOnFaulted); } public static Task OnError(this Task task, Action onError) { Task DoIt(Task t) { onError(); return t; } return task.ContinueWith(DoIt, OnlyOnFaulted).Unwrap(); } public static Task OnError(this Task task, Action onError) { Task DoIt(Task t) { onError(t.Exception!); return t; } return task.ContinueWith(DoIt, OnlyOnFaulted).Unwrap(); } public static Task OnError(this Task task, Func action) { return task .ContinueWith(t => Task.FromException(action(t.Exception!)), OnlyOnFaulted) .Unwrap(); } public static Task Then(this Task task, Func> func) { return task.ContinueWith(t => func(t.Result), OnlyOnRanToCompletion).Unwrap(); } public static Task Then(this Task task, Func func) { return task.ContinueWith(t => func(t.Result), OnlyOnRanToCompletion); } public static Task Then(this Task task, Func func) { return task.ContinueWith(_ => func(), OnlyOnRanToCompletion); } public static Task Then(this Task task, R r) { return task.ContinueWith(_ => r, OnlyOnRanToCompletion); } public static Task Then(this Task task, Action action) { T DoIt(Task t) { action(); return t.Result; } return task.ContinueWith(DoIt, OnlyOnRanToCompletion); } public static Task Then(this Task task, Action action) { T DoIt(Task t) { action(t.Result); return t.Result; } return task.ContinueWith(DoIt, OnlyOnRanToCompletion); } public static Task Match(this Task task, Func onSuccess, Func onError, Func onAborted) { return task.ContinueWith(t => t.Status switch { RanToCompletion => onSuccess(t.Result), Faulted => onError(t.Exception!), _ => onAborted(), }); } // public static Task Do(this Task task, // Action onSuccess, // Action onError, // Action onAborted) // { // return task.ContinueWith(t => t.Status switch // { // RanToCompletion => onSuccess(t.Result), // Faulted => onError(t.Exception!), // _ => onAborted(), // }); // } // public static Task MatchAsync(this Task task, // Func>? onSuccess, // Func>? onError, // Func>? onAborted) // { // return task.ContinueWith(t => t.Status switch // { // RanToCompletion => onSuccess is not null ? onSuccess(t.Result) : , // Faulted => onError(t.Exception!), // _ => onAborted() // }).Unwrap(); // } public static Task Return(this Task task, R r) { return task.ContinueWith(_ => r, OnlyOnRanToCompletion); } public static Task Catch(this Task task, Action onException) { return task.ContinueWith(t => onException(t.Exception!), OnlyOnFaulted); } public static Task Catch(this Task task, T onException) { return task.ContinueWith(_ => onException, OnlyOnFaulted); } public static Task Catch(this Task task, Func onException) { return task.ContinueWith(t => onException(t.Exception!), OnlyOnFaulted); } }