In this short blog post, I will show you 5 useful extensions for Task
1. Fire and forget
Sometimes you want to fire and forget a task. This means that you want to start a task but you don't want to wait for it to finish. This is useful when you want to start a task but you don't care about the result (non-critical tasks). For example when you want to start a task that sends an email. You don't want to wait for the email to be sent before you can continue with your code. So you can use the FireAndForget
extension method to start the task and forget about it. Optionally you can pass an error handler to the method. This error handler will be called when the task throws an exception.
public static void FireAndForget(
this Task task,
Action<Exception> errorHandler = null)
{
task.ContinueWith(t =>
{
if (t.IsFaulted && errorHandler != null)
errorHandler(t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
Usage:
SendEmailAsync().FireAndForget(errorHandler => Console.WriteLine(errorHandler.Message));
2. Retry
If you want to retry a task a specific number of times, you can use the Retry
extension method. This method will retry the task until it succeeds or the maximum number of retries is reached. You can pass a delay between retries. This delay will be used between each retry.
public static async Task<TResult> Retry<TResult>(this Func<Task<TResult>> taskFactory, int maxRetries, TimeSpan delay)
{
for (int i = 0; i < maxRetries; i++)
{
try
{
return await taskFactory().ConfigureAwait(false);
}
catch
{
if (i == maxRetries - 1)
throw;
await Task.Delay(delay).ConfigureAwait(false);
}
}
return default(TResult); // Should not be reached
}
Usage:
var result = await (() => GetResultAsync()).Retry(3, TimeSpan.FromSeconds(1));
3. OnFailure
Executes a callback function when a Task encounters an exception.
public static async Task OnFailure(this Task task, Action<Exception> onFailure)
{
try
{
await task.ConfigureAwait(false);
}
catch (Exception ex)
{
onFailure(ex);
}
}
Usage:
await GetResultAsync().OnFailure(ex => Console.WriteLine(ex.Message));
4. Timeout
Sometimes you want to set a timeout for a task. This is useful when you want to prevent a task from running for too long. You can use the Timeout
extension method to set a timeout for a task. If the task takes longer than the timeout the task will be cancelled.
public static async Task WithTimeout(this Task task, TimeSpan timeout)
{
var delayTask = Task.Delay(timeout);
var completedTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
if (completedTask == delayTask)
throw new TimeoutException();
await task;
}
Usage:
await GetResultAsync().WithTimeout(TimeSpan.FromSeconds(1));
Note: Since .NET 6 you can use WaitAsync
.
5. Fallback
Sometimes you want to use a fallback value when a task fails. You can use the Fallback
extension method to use a fallback value when a task fails.
public static async Task<TResult> Fallback<TResult>(this Task<TResult> task, TResult fallbackValue)
{
try
{
return await task.ConfigureAwait(false);
}
catch
{
return fallbackValue;
}
}
Usage:
var result = await GetResultAsync().Fallback("fallback");
Conclusion
There we have it: Five useful extensions for TaskValueTask
if you want to. If you have more, please let me know!