A static class containing extensions for Func<..., Task>
that have multiple handlers registered. (Typically when e.g., func += anotherFunc
)
These methods would solve following problems:
- parallel invocation (
Task.WhenAll
), even for sync actions. - Waiting for Execution
- Returning
AggregateException
instead of the first inner exception (default byawait
)
Currently support generic version up to 3 arguments.
Following options are supported:
- WhenAll: run tasks until all finished. Throw
AggregateException
at the end if exception(s) occurred. - WhenAny and throw
AggregateException
on first exception: Rest tasks will keep running at background, or you cancel them if your funcs supportCancellationToken
. - Sequential: run tasks in registration sequence. throw on first exception. Rest tasks will not be executed. not recommended due to low efficiency.
Func<Task> waitDelegates = null!;
waitDelegates += async () => await Utils.DelayAndPrint(3000);
waitDelegates += async () =>
{
await Utils.DelayAndPrint(200);
throw new Exception("exception from 2nd handler");
};
waitDelegates += async () =>
{
await Utils.DelayAndPrint(500);
throw new Exception("exception from 3rd handler");
};
waitDelegates += async () => await Utils.DelayAndPrint(1000);
try
{
await waitDelegates.InvokeAsync(true); // first exception will be thrown, and the rest tasks keep running in background (or you cancel them by yourself)
}
catch(AggregateException ex)
{
Console.WriteLine($"exception count: {ex.InnerExceptions.Count}"); // 1
await Task.Delay(3000);
}
// await waitDelegates(); // does not work as expected, only the last handler will be awaited and others run in background, exceptions will be swallowed
// await waitDelegates.InvokeAsync(); // exception will be thrown when all operations are finished
// await waitDelegates.InvokeAsync(true); // first exception will be thrown, and the rest tasks keep running in background (or you cancel them by yourself)
// await waitDelegates.SequentialInvokeAsync(); // first exception will be thrown, and the rest tasks will not be executed.
Console.WriteLine("WaitDelegates finished");
The Utils
class:
internal static class Utils
{
public static void SleepAndPrint(int milliSec)
{
Console.WriteLine($"Start sleep for {milliSec}ms, current thread: {Environment.CurrentManagedThreadId}");
Thread.Sleep(milliSec);
Console.WriteLine($"Stop sleep for {milliSec}ms, current thread: {Environment.CurrentManagedThreadId}");
}
public static async Task DelayAndPrint(int milliSec, CancellationToken ct = default)
{
Console.WriteLine($"Start delay for {milliSec}ms, current thread: {Environment.CurrentManagedThreadId}");
await Task.Delay(milliSec, ct);
Console.WriteLine($"Stop delay for {milliSec}ms, current thread: {Environment.CurrentManagedThreadId}");
}
}
- Convention:
CancellationToken
should always be the last or the second last (only when the last isparams
) argument of the func if there is any.