Skip to content

Instantly share code, notes, and snippets.

@Rmanaf
Last active September 11, 2024 04:58
Show Gist options
  • Save Rmanaf/4012c30a206029951bd42cbc542487fb to your computer and use it in GitHub Desktop.
Save Rmanaf/4012c30a206029951bd42cbc542487fb to your computer and use it in GitHub Desktop.
A collection of extension methods for the Task class
/// <summary>
/// A collection of extension methods for the Task class
/// </summary>
public static class TaskExtensions
{
/// <summary>
/// Introduces a delay before continuing with the next task.
/// This method is used for non-generic tasks (Task without a result).
/// </summary>
/// <param name="task">The task to delay after.</param>
/// <param name="milliseconds">The amount of time to delay in milliseconds.</param>
/// <param name="continueOnCapturedContext">
/// If true, the continuation will run on the captured synchronization context (e.g., UI thread if applicable).
/// </param>
/// <returns>A new task that will complete after the delay.</returns>
public static Task Delay(this Task task, int milliseconds, bool continueOnCapturedContext = true)
{
return task.ContinueWith(async t =>
{
await Task.Delay(milliseconds);
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
}
/// <summary>
/// Introduces a delay before continuing with the next task.
/// This method is used for non-generic tasks (Task without a result).
/// </summary>
/// <param name="task">The task to delay after.</param>
/// <param name="timeSpan">The amount of time to delay as a TimeSpan.</param>
/// <param name="continueOnCapturedContext">
/// If true, the continuation will run on the captured synchronization context (e.g., UI thread if applicable).
/// </param>
/// <returns>A new task that will complete after the delay.</returns>
public static Task Delay(this Task task, TimeSpan timeSpan, bool continueOnCapturedContext = true)
{
return task.ContinueWith(async t =>
{
await Task.Delay(timeSpan);
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
}
/// <summary>
/// Introduces a delay before continuing with the next task.
/// This method is used for tasks that return a result (Task<T>).
/// The result of the previous task is passed forward after the delay.
/// </summary>
/// <typeparam name="T">The type of the result produced by the task.</typeparam>
/// <param name="task">The task to delay after.</param>
/// <param name="milliseconds">The amount of time to delay in milliseconds.</param>
/// <param name="continueOnCapturedContext">
/// If true, the continuation will run on the captured synchronization context (e.g., UI thread if applicable).
/// </param>
/// <returns>A new task that will complete after the delay and will pass forward the result of the previous task.</returns>
public static Task<T> Delay<T>(this Task<T> task, int milliseconds, bool continueOnCapturedContext = true)
{
return task.ContinueWith(async t =>
{
var result = t.Result; // Capture the result of the task
await Task.Delay(milliseconds); // Delay for the specified time
return result; // Return the result after the delay
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
}
/// <summary>
/// Introduces a delay before continuing with the next task.
/// This method is used for tasks that return a result (Task<T>).
/// The result of the previous task is passed forward after the delay.
/// </summary>
/// <typeparam name="T">The type of the result produced by the task.</typeparam>
/// <param name="task">The task to delay after.</param>
/// <param name="timeSpan">The amount of time to delay as a TimeSpan.</param>
/// <param name="continueOnCapturedContext">
/// If true, the continuation will run on the captured synchronization context (e.g., UI thread if applicable).
/// </param>
/// <returns>A new task that will complete after the delay and will pass forward the result of the previous task.</returns>
public static Task<T> Delay<T>(this Task<T> task, TimeSpan timeSpan, bool continueOnCapturedContext = true)
{
return task.ContinueWith(async t =>
{
var result = t.Result; // Capture the result of the task
await Task.Delay(timeSpan); // Delay for the specified TimeSpan
return result; // Return the result after the delay
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
}
/// <summary>
/// Executes the given action if the task completes successfully.
/// </summary>
/// <param name="task">The task to chain the success handler to.</param>
/// <param name="onSuccess">The action to execute on task success.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the success action if the original task completes successfully.</returns>
public static Task Then(this Task task, Action onSuccess, bool continueOnCapturedContext = true)
{
task.ContinueWith(t =>
{
if (t.IsCompletedSuccessfully)
{
onSuccess();
}
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default);
return task;
}
/// <summary>
/// Executes the given action with the result if the task completes successfully.
/// </summary>
/// <typeparam name="T">The type of the result of the task.</typeparam>
/// <param name="task">The task to chain the success handler to.</param>
/// <param name="onSuccess">The action to execute with the result on task success.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the success action with the result if the original task completes successfully.</returns>
public static Task Then<T>(this Task<T> task, Action<T> onSuccess, bool continueOnCapturedContext = true)
{
task.ContinueWith(t =>
{
if (t.IsCompletedSuccessfully)
{
onSuccess(t.Result);
}
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default);
return task;
}
/// <summary>
/// Asynchronously executes the given function if the task completes successfully.
/// </summary>
/// <param name="task">The task to chain the async success handler to.</param>
/// <param name="onSuccessAsync">The async function to execute on task success.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the async success function if the original task completes successfully.</returns>
public static Task Then(this Task task, Func<Task> onSuccessAsync, bool continueOnCapturedContext = true)
{
task.ContinueWith(async t =>
{
if (t.IsCompletedSuccessfully)
{
await onSuccessAsync();
}
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
return task;
}
/// <summary>
/// Asynchronously executes the given function with the result if the task completes successfully.
/// </summary>
/// <typeparam name="T">The type of the result of the task.</typeparam>
/// <param name="task">The task to chain the async success handler to.</param>
/// <param name="onSuccessAsync">The async function to execute with the result on task success.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the async success function with the result if the original task completes successfully.</returns>
public static Task Then<T>(this Task<T> task, Func<T, Task> onSuccessAsync, bool continueOnCapturedContext = true)
{
task.ContinueWith(async t =>
{
if (t.IsCompletedSuccessfully)
{
await onSuccessAsync(t.Result);
}
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
return task;
}
/// <summary>
/// Executes the given action if the task encounters an error.
/// </summary>
/// <param name="task">The task to chain the error handler to.</param>
/// <param name="onError">The action to execute on task failure.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the error action if the original task fails.</returns>
public static Task Catch(this Task task, Action<Exception> onError, bool continueOnCapturedContext = true)
{
task.ContinueWith(t =>
{
if (t.IsFaulted && t.Exception != null)
{
onError(t.Exception.InnerException ?? t.Exception);
}
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default);
return task;
}
/// <summary>
/// Asynchronously executes the given function if the task encounters an error.
/// </summary>
/// <param name="task">The task to chain the async error handler to.</param>
/// <param name="onErrorAsync">The async function to execute on task failure.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the async error function if the original task fails.</returns>
public static Task Catch(this Task task, Func<Exception, Task> onErrorAsync, bool continueOnCapturedContext = true)
{
task.ContinueWith(async t =>
{
if (t.IsFaulted && t.Exception != null)
{
await onErrorAsync(t.Exception.InnerException ?? t.Exception); // Handle task failure asynchronously
}
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
return task;
}
/// <summary>
/// Executes the given action after the task completes, regardless of success or failure.
/// </summary>
/// <param name="task">The task to chain the final action to.</param>
/// <param name="onFinally">The action to execute after task completion.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the final action after the original task completes.</returns>
public static Task Finally(this Task task, Action onFinally, bool continueOnCapturedContext = true)
{
task.ContinueWith(t =>
{
onFinally();
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default);
return task;
}
/// <summary>
/// Asynchronously executes the given function after the task completes, regardless of success or failure.
/// </summary>
/// <param name="task">The task to chain the final async function to.</param>
/// <param name="onFinallyAsync">The async function to execute after task completion.</param>
/// <param name="continueOnCapturedContext">Whether to continue on the captured context (typically UI thread).</param>
/// <returns>A task that will execute the final async function after the original task completes.</returns>
public static Task Finally(this Task task, Func<Task> onFinallyAsync, bool continueOnCapturedContext = true)
{
task.ContinueWith(async t =>
{
await onFinallyAsync();
}, continueOnCapturedContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default).Unwrap();
return task;
}
}
@Rmanaf
Copy link
Author

Rmanaf commented Sep 10, 2024

Usage

Task.Run(() => Console.WriteLine("Start"))
    .Delay(1000) // Delay for 1 second
    .Then(() => Console.WriteLine("After a short delay"))
    .Catch(ex => Console.WriteLine($"Error: {ex.Message}"))
    .Finally(() => Console.WriteLine("Completed"));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment