Skip to content

Instantly share code, notes, and snippets.

@d1820
Created October 24, 2023 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d1820/7bfc043d12328c4214608f86038b4d3a to your computer and use it in GitHub Desktop.
Save d1820/7bfc043d12328c4214608f86038b4d3a to your computer and use it in GitHub Desktop.
.Net Task Extensions
// we use this to group them all as available based on already using this namespace
namespace Common.Extensions
{
/// <summary>
/// adapted from https://archive.ph/2022.10.14-213608/https://betterprogramming.pub/my-top-7-custom-extension-methods-for-net-7-and-c-494acb2e6634#selection-1935.0-2339.1
/// </summary>
public static class TaskExtensions
{
/// <summary>
/// Wraps the executing task in a try/catch
/// </summary>
/// <param name="task"> The task. </param>
/// <param name="errorHandler"> The error handler. </param>
/// <param name="rethrow"> If true, rethrow. </param>
/// <returns> A Task. </returns>
public static async Task TryAsync(this Task task, Action<Exception>? errorHandler = null, bool rethrow = false)
{
try
{
await task;
}
catch (Exception ex)
{
errorHandler?.Invoke(ex);
if (rethrow)
{
throw;
}
}
}
/// <summary>
/// Wraps the executing task in a try/catch
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="task"> The task. </param>
/// <param name="errorHandler"> The error handler. </param>
/// <param name="rethrow"> If true, rethrow. </param>
/// <returns> <![CDATA[Task<T>]]> </returns>
public static async Task<T?> TryAsync<T>(this Task<T?> task, Action<Exception>? errorHandler = null, bool rethrow = false) where T : class
{
try
{
return await task;
}
catch (Exception ex)
{
errorHandler?.Invoke(ex);
if (rethrow)
{
throw;
}
}
return default;
}
/// <summary>
/// Returns when all tasks are completed asynchronously.
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="tasks"> The tasks. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[Task<IEnumerable<T>>]]> </returns>
public static async Task<IEnumerable<T>> WhenAllAsync<T>(this IEnumerable<Task<T>> tasks)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
return await Task
.WhenAll(tasks)
.ConfigureAwait(false);
}
/// <summary>
/// Returns when all tasks are completed asynchronously.
/// </summary>
/// <param name="tasks"> The tasks. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> A Task. </returns>
public static Task WhenAllAsync(this IEnumerable<Task> tasks)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
return Task
.WhenAll(tasks);
}
/// <summary>
/// Returns when all tasks are completed in sequentially asynchronously.
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="tasks"> The tasks. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[Task<IEnumerable<T>>]]> </returns>
public static async Task<IEnumerable<T>> WhenAllSequentialAsync<T>(this IEnumerable<Task<T>> tasks)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
var results = new List<T>();
foreach (var task in tasks)
{
results.Add(await task.ConfigureAwait(false));
}
return results;
}
/// <summary>
/// Returns when all tasks are completed in sequentially asynchronously.
/// </summary>
/// <param name="tasks"> The tasks. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> A Task. </returns>
public static async Task WhenAllSequentialAsync(this IEnumerable<Task> tasks)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
foreach (var task in tasks)
{
await task.ConfigureAwait(false);
}
}
/// <summary>
/// Returns when all tasks are completed in parallel asynchronously.
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="tasks"> The tasks. </param>
/// <param name="maxDegreeOfParallelism"> The degree. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[Task<IEnumerable<T>>]]> </returns>
public static async Task<IEnumerable<T>> WhenAllParallelAsync<T>(this IEnumerable<Task<T>> tasks, int maxDegreeOfParallelism)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
var results = new List<T>();
foreach (var chunk in tasks.Chunk(maxDegreeOfParallelism))
{
var chunkResults = await Task.WhenAll(chunk).ConfigureAwait(false);
results.AddRange(chunkResults);
}
return results;
}
/// <summary>
/// Returns when all tasks are completed in parallel asynchronously.
/// </summary>
/// <param name="tasks"> The tasks. </param>
/// <param name="maxDegreeOfParallelism"> The degree. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> A Task. </returns>
public static async Task WhenAllParallelAsync(this IEnumerable<Task> tasks, int maxDegreeOfParallelism)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
foreach (var chunk in tasks.Chunk(maxDegreeOfParallelism))
{
await Task.WhenAll(chunk).ConfigureAwait(false);
}
}
/// <summary>
/// Executes a callback after task completion and passes in the result from the task
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="task"> The task. </param>
/// <param name="tapAsync"> The tap asynchronously. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[Task<T>]]> </returns>
public static async Task<T> ThenCallAsync<T>(this Task<T> task, Func<T, Task> tapAsync)
{
if (task is null)
{
throw new ArgumentNullException(nameof(task));
}
if (tapAsync is null)
{
throw new ArgumentNullException(nameof(tapAsync));
}
var res = await task;
await tapAsync(res);
return res;
}
/// <summary>
/// Executes a callback after task completion and passes in the result from the task
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="task"> The task. </param>
/// <param name="tap"> The tap. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[Task<T>]]> </returns>
public static async Task<T> ThenCallAsync<T>(this Task<T> task, Action<T> tap)
{
if (task is null)
{
throw new ArgumentNullException(nameof(task));
}
if (tap is null)
{
throw new ArgumentNullException(nameof(tap));
}
var res = await task;
tap(res);
return res;
}
/// <summary>
/// Gets the results.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tasks">The tasks.</param>
/// <param name="tap">The tap.</param>
/// <exception cref="ArgumentNullException"></exception>
/// <returns><![CDATA[Task<IEnumerable<T>>]]></returns>
public static async Task<IEnumerable<T>> GetResultsAsync<T>(this IEnumerable<Task<T>> tasks, Action<T>? tap = null)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
var items = new List<T>();
foreach (var t in tasks)
{
var result = await t;
tap?.Invoke(result);
items.Add(result);
}
return items;
}
}
}
// we use this to group them all as available based on already using this namespace
namespace Common.Extensions
{
/// <summary>
/// adapted from https://archive.ph/2022.10.14-213608/https://betterprogramming.pub/my-top-7-custom-extension-methods-for-net-7-and-c-494acb2e6634#selection-1935.0-2339.1
/// </summary>
public static class ValueTaskExtensions
{
/// <summary>
/// Wraps the executing task in a try/catch
/// </summary>
/// <param name="task"> The task. </param>
/// <param name="errorHandler"> The error handler. </param>
/// <param name="rethrow"> If true, rethrow. </param>
/// <returns> A ValueTask. </returns>
public static async ValueTask TryAsync(this ValueTask task, Action<Exception>? errorHandler = null, bool rethrow = false)
{
try
{
await task.ConfigureAwait(false);
}
catch (Exception ex)
{
errorHandler?.Invoke(ex);
if (rethrow)
{
throw;
}
}
}
/// <summary>
/// Wraps the executing task in a try/catch
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="task"> The task. </param>
/// <param name="errorHandler"> The error handler. </param>
/// <param name="rethrow"> If true, rethrow. </param>
/// <returns> <![CDATA[ValueTask<T>]]> </returns>
public static async ValueTask<T?> TryAsync<T>(this ValueTask<T?> task, Action<Exception>? errorHandler = null, bool rethrow = false) where T : class
{
try
{
return await task.ConfigureAwait(false);
}
catch (Exception ex)
{
errorHandler?.Invoke(ex);
if (rethrow)
{
throw;
}
}
return default;
}
/// <summary>
/// Returns when all tasks are completed in sequentially asynchronously.
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="tasks"> The tasks. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[ValueTask<IEnumerable<T>>]]> </returns>
public static async ValueTask<IEnumerable<T>> WhenAllSequentialAsync<T>(this IEnumerable<ValueTask<T>> tasks)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
var results = new List<T>();
foreach (var task in tasks)
{
results.Add(await task.ConfigureAwait(false));
}
return results;
}
/// <summary>
/// Returns when all tasks are completed in sequentially asynchronously.
/// </summary>
/// <param name="tasks"> The tasks. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> A ValueTask. </returns>
public static async ValueTask WhenAllSequentialAsync(this IEnumerable<ValueTask> tasks)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
foreach (var task in tasks)
{
await task.ConfigureAwait(false);
}
}
/// <summary>
/// Executes a callback after task completion and passes in the result from the task
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="task"> The task. </param>
/// <param name="tapAsync"> The tap asynchronously. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[ValueTask<T>]]> </returns>
public static async ValueTask<T> ThenCallAsync<T>(this ValueTask<T> task, Func<T, ValueTask> tapAsync)
{
if (tapAsync is null)
{
throw new ArgumentNullException(nameof(tapAsync));
}
var res = await task.ConfigureAwait(false);
await tapAsync(res).ConfigureAwait(false);
return res;
}
/// <summary>
/// Executes a callback after task completion and passes in the result from the task
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="task"> The task. </param>
/// <param name="tap"> The tap. </param>
/// <exception cref="ArgumentNullException"> </exception>
/// <returns> <![CDATA[ValueTask<T>]]> </returns>
public static async ValueTask<T> ThenCallAsync<T>(this ValueTask<T> task, Action<T> tap)
{
if (tap is null)
{
throw new ArgumentNullException(nameof(tap));
}
var res = await task.ConfigureAwait(false);
tap(res);
return res;
}
/// <summary>
/// Gets the results.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tasks">The tasks.</param>
/// <param name="tap">The tap.</param>
/// <exception cref="ArgumentNullException"></exception>
/// <returns><![CDATA[ValueTask<IEnumerable<T>>]]></returns>
public static async ValueTask<IEnumerable<T>> GetResultsAsync<T>(this IEnumerable<ValueTask<T>> tasks, Action<T>? tap = null)
{
if (tasks is null)
{
throw new ArgumentNullException(nameof(tasks));
}
var items = new List<T>();
foreach (var t in tasks)
{
var result = await t;
tap?.Invoke(result);
items.Add(result);
}
return items;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment