Sharpnado.TaskMonitor 1.1.0

dotnet add package Sharpnado.TaskMonitor --version 1.1.0
                    
NuGet\Install-Package Sharpnado.TaskMonitor -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sharpnado.TaskMonitor" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sharpnado.TaskMonitor" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Sharpnado.TaskMonitor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sharpnado.TaskMonitor --version 1.1.0
                    
#r "nuget: Sharpnado.TaskMonitor, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sharpnado.TaskMonitor@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sharpnado.TaskMonitor&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Sharpnado.TaskMonitor&version=1.1.0
                    
Install as a Cake Tool

Sharpnado.TaskMonitor

task-monitor

TaskMonitor is a task wrapper component helping you to deal with "fire and forget" Task (non awaited task) by implementing async/await best practices.

Sharpnado.TaskMonitor

It offers:

  • Safe execution of all your async tasks: taylor-made for async void scenarios and non awaited Task
  • Callbacks for any states (canceled, success, completed, failure)
  • Default or custom error handler
  • Default or custom task statistics logger

Free yourself from async void!

Now let's say you have an evil async void in your code:

public async void InitializeAsync()
{
    try
    {
        await InitializeMonkeyAsync();
    }
    catch (Exception exception)
    {
        // handle error
    }
}

private async Task InitializeMonkeyAsync()
{
    Monkey = await _monkeyService.GetMonkeyAsync();
}

You can get rid of async void and simply use the TaskMonitor:

public void InitializeAsync()
{
    TaskMonitor.Create(InitializeMonkeyAsync);
}

If an error occurs, it will call the default error handler which will Trace the exception, so that it won't crash (async void) or fail silently (non awaited task).

TaskMonitor|ERROR|013|Error in wrapped task
Exception:System.Exception: Fault
   at Sharpnado.Tasks.Tests.TaskMonitorTest.DelayFaultAsync() in D:\Dev\Sharpnado\src\TaskMonitor\Sharpnado.TaskMonitor.Tests\TaskMonitorTest.cs:line 243
   at Sharpnado.Tasks.TaskMonitorBase.MonitorTaskAsync(Task task) in D:\Dev\Sharpnado\src\TaskMonitor\Sharpnado.TaskMonitor\TaskMonitorBase.cs:line 186

But you can also setup globally your own error handler with the TaskMonitorConfiguration static class:

TaskMonitorConfiguration.ErrorHandler = (message, exception) =>
    {
        // Do custom stuff for exception handling;
    };

Now careful: if you are in a MVVM scenario, I strongly encourage you to read my Free Yourself From IsBusy post. The ViewModelLoader or the NotifyTask would be better to handle the ViewModel loading states.

Features summary

Delegates for all states of the ran task:

// Here task is "hot", it runs as soon as Create is called
TaskMonitor.Create(
    () => DoSomethingAsync(cts.Token), 
    t => isCompleted = true, 
    t => isFaulted = true,
    t => isSuccessfullyCompleted = true);

Builder for more elegant construction and deferred execution:

var monitor = new TaskMonitor.Builder(() => DoSomethingAsync(cts.Token))
    .WithWhenCanceled(t => isCanceled = true)
    .WithWhenFaulted(t => isFaulted = true)
    .WithWhenSuccessfullyCompleted(t => isSuccess = true)
    .Build();

// explicit task start
monitor.Start();

Support for task with result, Task<T>:

var monitor = TaskMonitor<List<string>>.Create(
    DelayListAsync, 
    t => isCompleted = true, 
    t => isFaulted = true,
    (task, result) =>
    {
        isSuccessfullyCompleted = true;
        Assert.Equal(3, result.Count);
    });

private async Task<List<string>> DelayListAsync()
{
    await Task.Delay(200);
    return new List<string> {"1", "2", "3"};
}

Default handling of errors and statistics, and naming of the monitor:

TaskMonitorConfiguration.LogStatistics = true;
TaskMonitor.Create(
    DelayFaultAsync,
    name: "NominalFaultTestTask");

Output:

TaskMonitor|ERROR|013|Error in wrapped task
Exception:System.Exception: Fault
   at Sharpnado.Tasks.Tests.TaskMonitorTest.DelayFaultAsync() in D:\Dev\Sharpnado\src\TaskMonitor\Sharpnado.TaskMonitor.Tests\TaskMonitorTest.cs:line 262
   at Sharpnado.Tasks.TaskMonitorBase.MonitorTaskAsync(Task task) in D:\Dev\Sharpnado\src\TaskMonitor\Sharpnado.TaskMonitor\TaskMonitorBase.cs:line 186
TaskMonitor|STATS|013|NominalFaultTestTask => Status: IsFaulted, Executed in 246,55870000000002 ms

Global configuration for statistics and errors handlers:

TaskMonitorConfiguration.LogStatistics = true;
TaskMonitorConfiguration.StatisticsHandler = (taskMonitor, timeSpan) =>
{
    statsHandlerCalled = true;
    Assert.True(timeSpan.TotalMilliseconds > 0);
};

TaskMonitorConfiguration.ErrorHandler = (taskMonitor, message, exception) =>
{
    errorHandlerCalled = true;
};

Run the wrapped Task in a new Task:

int threadId = Thread.CurrentThread.ManagedThreadId;
var monitor = TaskMonitor<int>.Create(
    DelayThreadIdAsync,
    inNewTask: true);

await monitor.TaskCompleted;

Assert.NotEqual(threadId, monitor.Result);

Await the task monitor without failures. Awaiting on the TaskCompleted property will never fail:

// Will always succeed wether the task is canceled, successful or faulted
await monitor.TaskCompleted;

Consider globally or locally the cancel state as faulted to simplify your workflow:

// Local configuration
var cts = new CancellationTokenSource();
bool isFaulted = false;
bool isCanceled = false;

var monitor = new TaskMonitor.Builder(() => DelayCanceledAsync(cts.Token))
    .WithWhenCanceled(t => isCanceled = true)
    .WithWhenFaulted(t => isFaulted = true)
    .WithConsiderCanceledAsFaulted(true)
    .Build();

cts.Cancel();
monitor.Start();

await monitor.TaskCompleted;

Assert.True(!isCanceled && monitor.IsCanceled);
Assert.True(isFaulted && monitor.IsFaulted);
// Global configuration
var cts = new CancellationTokenSource();
TaskMonitorConfiguration.ConsiderCanceledAsFaulted = true;
bool isFaulted = false;
bool isCanceled = false;
bool isSuccess = false;

var monitor = new TaskMonitor.Builder(() => DelayCanceledAsync(cts.Token))
    .WithWhenCanceled(t => isCanceled = true)
    .WithWhenFaulted(t => isFaulted = true)
    .Build();

cts.Cancel();
monitor.Start();

await monitor.TaskCompleted;

Assert.True(!isCanceled && monitor.IsCanceled);
Assert.True(isFaulted && monitor.IsFaulted);

Other common scenarios

Good with events or messages

If you are subscribing to an event/message and want to make async stuff when the event is raised, it will also be a perfect candidate.


public Constructor(IMonkeyService monkeyService)
{
    monkeyService.MonkeyChanged += OnMonkeyChanged;
    // same as messageService.Subscribe("MonkeyChangedMessage", OnMonkeyChanged)
}

private void OnMonkeyChanged(MonkeyChangedEventArgs eventArgs)
{
    TaskMonitor.Create(() => DoSomethingAsync(eventArgs.Monkey));
}

private Task DoSomethingAsync(Monkey monkey)
{
    await CrazyAsyncStuff(monkey);
    await SomeOtherCrazyAction();
}

Good with non awaited task and ContinueWith

Previously you maybe used the ContinueWith task method to create a new task and deal with fire and forget scenarios.

public void DoSomethingAsync()
{
    // Task will not be awaited and you are still handling the exception: hooray!
    Task.Run(() => InitializeMonkey())
        .ContinueWith(
            t => HandleException(t.InnerException),
            TaskContinuationOptions.OnlyOnFaulted);
}

private void InitializeMonkey()
{
    ...
}

You can achieve the same behaviour with the TaskMonitor in a cleaner way:

public void DoSomethingAsync()
{
    TaskMonitor.Create(Task.Run(() => InitializeMonkey()));
}

Can be used as a simple decorator for statistics and error handling

You can specify global error handler and statistics logger:

TaskMonitorConfiguration.LogStatistics = true;
TaskMonitorConfiguration.StatisticsHandler = (taskMonitor, timeSpan) =>
{
    // My global statistics logger
};

TaskMonitorConfiguration.ErrorHandler = (taskMonitor, message, exception) =>
{
    // My global error logger
};

Then use TaskMonitor as a simple task logging decorator:

try
{
    await TaskMonitor.Create(DelayFaultAsync, name: "UseMonitorAsDecoratedFaultTest").Task;
}
catch(Exception exception)
{
    // handle exception
}

But you can also let the default handlers Trace the errors and the statistics. Output with default handlers:

TaskMonitor|ERROR|013|Error in wrapped task
Exception:System.Exception: Fault
   at Sharpnado.Tasks.Tests.TaskMonitorTest.DelayFaultAsync() in D:\Dev\Sharpnado\src\TaskMonitor\Sharpnado.TaskMonitor.Tests\TaskMonitorTest.cs:line 262
   at Xunit.Assert.RecordExceptionAsync(Func`1 testCode) in C:\Dev\xunit\xunit\src\xunit.assert\Asserts\Record.cs:line 82
TaskMonitor|STATS|013|UseMonitorAsDecoratedFaultTest => Status: IsFaulted, Executed in 334,27070000000003 ms

Origins

The TaskMonitor was inspired by Stephen Cleary's NotifyTask. It's a task wrapper dealing with non-awaited, or fire and forget if you prefer, Task. The difference is that NotifyTask is made for UI scenarios (MVVM), where you want to bind the result or the state of the task to a view property. For this it implements INotifyPropertyChanged. See this: https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

Whereas TaskMonitor is designed to be used in any kind of scenarios (server, business layer, UI, etc...).

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages (15)

Showing the top 5 NuGet packages that depend on Sharpnado.TaskMonitor:

Package Downloads
Sharpnado.Presentation.Forms

Collection of Xamarin.Forms components. IMPORTANT: On platform projects, call SharpnadoInitializer.Initialize() after Xamarin.Forms.Forms.Init() and before LoadApplication(new App()). Pure Xamarin.Forms tabs: * Fixed tabs (android tabs style) * Scrollable tabs * Segmented tabs * Custom shadows (neumorphism ready) * Circle button in tab bar * Bottom bar tabs (ios tabs style) * Custom tabs (be creative just implement TabItem) * Independent ViewSwitcher Sharpnado.Shadows: * Add as many custom shadows as you like to any view (Android, iOS, UWP) * You can specify each shadow Color, Opacity, BlurRadius, and Offset * Simply implement Neumorphism * You can add one shadow, 3 shadows, 99 shadows, to any Xamarin.Forms element * Animate any of these property and make the shadows dance around your elements MaterialFrame: * AcrylicBlur mode * 3 Blur styles: Light, ExtraLight, Dark (UIVisualEffectView styles) * Acrylic mode * Dark mode * Light mode * Change modes dynamically * Performance (CALayer on ios, LayerDrawable on android) * Android: RealtimeBlurView from Tu Yimin (mmin18) The TaskLoaderView 2.0 handles all your task loading states: * Handles error with custom messages and icons * Handles empty states * Show snackbar errors for refresh scenarios (if data is already shown) * Handles retry with button * Support Xamarin.Forms.Skeleton * Can override any state views with your own custom ones HorizontalListView for Xamarin.Forms (close to a CollectionView): * Carousel layout * Column count * Snapping on first or middle element * Padding and item spacing * Handles NotifyCollectionChangedAction Add Remove and Reset actions * View recycling * RecyclerView on Android * UICollectionView on iOS Grid ListView (HorizontalListView with ListLayout set to Grid): * Column count * Drag And Drop * RefreshView support * Padding and item spacing * Handles NotifyCollectionChangedAction Add Remove and Reset actions * View recycling

Sharpnado.TaskLoaderView

Free yourself from IsBusy=true! The `TaskLoaderView` and the `TemplatedTaskLoader` are UI components that handle all your UI loading state (Loading, Error, Result, Notification), and removes all the pain of async loading from your view models (try catch / async void / IsBusy / HasErrors / base view models / ...) thanks to its brother the `TaskLoaderNotifier`. Featuring: * Default views for all loading states (Loading, Error, Success, Notification, Refresh) * Snackbar component * Compose notifiers with CompositeTaskLoaderNotifier * Stylable views including fonts, accent color, error images, ... * Any states are overridable with user custom views and easily positionned with AbsoluteLayout properties * Support for Xamarin.Forms.Skeleton nuget package * Support for refresh scenarios, and error while refreshing with the ErrorNotificationView * Supports Async mvvm ICommand through TaskLoaderCommand * Supports loading task on demand with the NotStarted state * TaskLoaderNotifier for the ViewModel side taking care of all the error handling and the IsBusy nonsense

Sharpnado.Forms.HorizontalListView

* Horizontal, Grid, Carousel or Vertical layout * Reveal custom animations * Drag and Drop feature * Column count * Infinite loading with Paginator component * Snapping on first or middle element * Padding and item spacing * Handles NotifyCollectionChangedAction Add, Remove and Reset actions * View recycling * RecyclerView on Android * UICollectionView on iOS Initialization: * On core project call Sharpnado.HorizontalListView.Initializer.Initialize(true, false) in App.xaml.cs after InitializeComponent(). * On platform projects (ios/android), call SharpnadoInitializer.Initialize() before Xamarin.Forms.Forms.Init() and LoadApplication(new App()). Warning: the MaterialFrame is no longer included in the package, you have to install it manually: Sharpnado.MaterialFrame.

Sharpnado.Tabs

Pure Xamarin.Forms tabs: * Fixed tabs (android tabs style) * Scrollable tabs * Vertical tabs * Material design tabs (top and leading icon) * Support for SVG images * Segmented tabs * Custom shadows (neumorphism ready) * Badges on tabs * Circle button in tab bar * Bottom bar tabs (ios tabs style) * Custom tabs (be creative just implement TabItem) * Independent ViewSwitcher * Bindable with ItemsSource -------------- Installation -------------- * In Core project, in `App.xaml.cs`: public App() { InitializeComponent(); Sharpnado.Tabs.Initializer.Initialize(loggerEnable: false); ... } * In iOS project: Xamarin.Forms.Forms.Init(); Sharpnado.Tabs.iOS.Preserver.Preserve(); * In UWP project: var rendererAssemblies = new[] { typeof(UWPShadowsRenderer).GetTypeInfo().Assembly, typeof(UwpTintableImageEffect).GetTypeInfo().Assembly, }; Xamarin.Forms.Forms.Init(e, rendererAssemblies);

Sharpnado.Tabs.Maui

Pure MAUI Tabs: * Fixed tabs (android tabs style) * Scrollable tabs * Vertical tabs * Material design tabs (top and leading icon) * Support for SVG images * Segmented tabs * Custom shadows (neumorphism ready) * Badges on tabs * Circle button in tab bar * Bottom bar tabs (ios tabs style) * Custom tabs (be creative just implement TabItem) * Independent ViewSwitcher * Bindable with ItemsSource ## Installation * In Core project, in `MauiProgram.cs`: ```csharp public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp() .UseSharpnadoTabs(loggerEnabled: false); } ```

GitHub repositories (6)

Showing the top 6 popular GitHub repositories that depend on Sharpnado.TaskMonitor:

Repository Stars
roubachof/Sharpnado.Tabs
Pure MAUI and Xamarin.Forms Tabs, including fixed tabs, scrollable tabs, bottom tabs, badge, segmented control, custom tabs, button tabs, bendable tabs...
roubachof/Sharpnado.MaterialFrame
A modern MAUI (and XF) Frame component supporting blur, acrylic, dark mode. Implemented with RealtimeBlurView on Android (custom blurview) and UIVisualEffectView on iOS.
roubachof/Sharpnado.Shadows
Add as many custom shadows (Color, Offset, Blur, Neumorphism) as you like to any Xamarin.Forms view (Android, iOS, UWP).
roubachof/Xamarin-Forms-Practices
Collection of good practices for Xamarin forms developement
roubachof/Sharpnado.CollectionView
A performant list view supporting: grid, horizontal and vertical layout, drag and drop, and reveal animations.
roubachof/Sharpnado.TaskLoaderView
Free yourself from IsBusy=true! The `TaskLoaderView` is a UI component that handles all your UI loading state (Loading, Error, Result, Notification), and removes all the pain of async loading from your view models (try catch / async void / IsBusy / HasErrors / base view models / ...) thanks to its brother the `TaskLoaderNotifier`.
Version Downloads Last Updated
1.1.0 11 8/11/2025
1.0.2 471,235 1/11/2021
1.0.1 165,248 8/2/2020
1.0.0 244,201 11/20/2019

Upgrade to .Net 9