Spargine is a collection of open-source assemblies and NuGet packages designed for .NET 10, which I have been developing and maintaining since the release of .NET Framework 2. These assemblies are not only a core part of my projects but are also actively deployed in production environments across several companies I collaborate with.
Get Spargine
You can access the source code and NuGet packages here:
- GitHub: Spargine 10
- NuGet: dotNetDaveNuGet
Asynchronous programming is essential in modern .NET applications, but working with Task can quickly become more complicated when you need to handle fire-and-forget operations, cancellation, aggregate exceptions, or timeouts.
The TaskExtensions class in DotNetTips.Spargine.Extensions provides a focused collection of extension methods designed to make these common asynchronous scenarios easier and safer to implement.
Whether you need to start a background operation without awaiting it, capture exceptions from fire-and-forget tasks, intentionally ignore cancellation, simplify an AggregateException, or prevent a task from waiting indefinitely, these extensions reduce repetitive plumbing while keeping your code’s intent clear.
Methods
FireAndForget()
Executes the specified Task as a fire-and-forget operation without awaiting its completion.
If the task faults, the exception is observed to help prevent unobserved task exceptions. This is useful when an asynchronous operation legitimately does not need to block or return a result to the caller.
FireAndForget(Action<Exception> action)
Executes the specified Task as a fire-and-forget operation while providing explicit exception handling.
If the task faults, the supplied Action<Exception> is invoked with the exception, allowing the application to log, report, or otherwise respond to failures without awaiting the task.
This overload is especially useful when a background operation should not interrupt the calling workflow but failures still need to be captured.
Example
The following example starts an asynchronous operation without awaiting it. If the task fails, the exception is passed to the supplied callback for handling.
public void SendNotification()
{
SendNotificationAsync()
.FireAndForget(exception =>
{
Console.WriteLine(
$"Background task failed: {exception.Message}");
});
}
private static async Task SendNotificationAsync()
{
await Task.Delay(500);
throw new InvalidOperationException(
"Unable to send notification.");
}
In this example, SendNotificationAsync() begins executing immediately, but the calling code does not wait for it to finish. If the operation throws an exception, FireAndForget() observes the fault and invokes the provided exception handler.
In a production application, the callback could instead send the exception to your application’s logging or telemetry system:
public void ProcessOrder(Order order)
{
ProcessOrderAsync(order)
.FireAndForget(exception =>
{
_logger.LogError(
exception,
"An error occurred while processing order {OrderId}.",
order.Id);
});
}
private async Task ProcessOrderAsync(Order order)
{
await _orderService.ProcessAsync(order);
}
This makes the failure visible without requiring the calling method to await the background operation.
A useful way to think about this overload is that fire-and-forget should not mean “fire and forget about errors.” It allows the caller to continue immediately while still providing a controlled way to observe and handle failures.
IgnoreCancellation()
Awaits and observes the task while suppressing OperationCanceledException.
Other exceptions are allowed to propagate normally, making this useful when cancellation represents an expected outcome rather than an application error.
Instead of surrounding expected cancellation with repetitive try/catch blocks, this extension makes that intent explicit at the call site.
UnwrapAggregate()
Simplifies exception handling when working with an AggregateException.
The method first flattens nested aggregate exceptions. If the resulting aggregate contains only a single inner exception, that exception is returned directly. If multiple exceptions remain, the flattened AggregateException is returned.
For exceptions that are not an AggregateException, the original exception is returned unchanged.
This makes exception inspection and logging easier while preserving multiple failures when they genuinely exist.
WithTimeoutAsync(TimeSpan timeout, CancellationToken cancellationToken)
Waits for a Task to complete within the specified timeout while optionally supporting cancellation.
If the operation does not complete before the timeout expires, a TimeoutException is thrown. The optional CancellationToken can independently cancel the wait.
This provides a clean way to prevent asynchronous operations from waiting indefinitely.
WithTimeoutAsync<T>(TimeSpan timeout, CancellationToken cancellationToken)
Provides the same timeout and cancellation behavior for Task<T> while preserving and returning the task’s result.
If the task completes within the specified timeout, its result is returned normally. If the timeout expires first, a TimeoutException is thrown.
Summary
The TaskExtensions class in Spargine takes several common—but often repetitive—asynchronous programming patterns and turns them into concise, expressive extension methods.
Instead of repeatedly writing continuation logic for fire-and-forget operations, try/catch blocks for expected cancellation, exception-flattening code, or timeout handling, developers can express those intentions directly through methods such as FireAndForget(), IgnoreCancellation(), UnwrapAggregate(), and WithTimeoutAsync().
The result is asynchronous code that is easier to read, easier to maintain, and clearer about how background work, cancellation, exceptions, and time limits should be handled.
If your application makes extensive use of Task and Task<T>, these small extensions can eliminate a surprising amount of boilerplate while making your asynchronous code’s behavior more explicit.
Get Involved!
The success of open-source projects like Spargine relies on community contributions. If you find these updates useful or have ideas for further improvements, I encourage you to contribute by:
- Submitting pull requests
- Reporting issues
- Suggesting new features
Your input is invaluable in making Spargine an even more powerful tool for the .NET community.
If you are interested in contributing or have any questions, feel free to contact me via email at dotnetdave@live.com. Your support and collaboration are greatly appreciated!
Thank you, and happy coding!
Pick up any books by David McCarter by going to Amazon.com: http://bit.ly/RockYourCodeBooks
Make a one-time donation
Make a monthly donation
Make a yearly donation
Choose an amount
Or enter a custom amount
Your contribution is appreciated.
Your contribution is appreciated.
Your contribution is appreciated.
If you liked this article, please buy David a cup of Coffee by going here: https://www.buymeacoffee.com/dotnetdave
© The information in this article is copywritten and cannot be reproduced in any way without express permission from David McCarter.
Discover more from dotNetTips.com
Subscribe to get the latest posts sent to your email.

