Com.MarcusTS.ResponsiveTasks
1.0.2
See the version list below for details.
dotnet add package Com.MarcusTS.ResponsiveTasks --version 1.0.2
NuGet\Install-Package Com.MarcusTS.ResponsiveTasks -Version 1.0.2
<PackageReference Include="Com.MarcusTS.ResponsiveTasks" Version="1.0.2" />
paket add Com.MarcusTS.ResponsiveTasks --version 1.0.2
#r "nuget: Com.MarcusTS.ResponsiveTasks, 1.0.2"
// Install Com.MarcusTS.ResponsiveTasks as a Cake Addin #addin nuget:?package=Com.MarcusTS.ResponsiveTasks&version=1.0.2 // Install Com.MarcusTS.ResponsiveTasks as a Cake Tool #tool nuget:?package=Com.MarcusTS.ResponsiveTasks&version=1.0.2
Fixing TPL, Part 1 of N: Responsive Tasks
When Microsoft announced the Task Parallel Library, C# programmers everywhere rejoiced. Simplified threads? Error handling within the library itself? What could possibly go wrong?
Just about everything, as it turns out.
A task requires a root in order to be properly awaited. For instance:
// An awaitable task
public Task YouCanAwaitMe() { }
// A root where you an await a task
public async Task IWillAwait()
{
await YouCanAwaitMe()
}
But there a lot of dead spots in an app where there is no root. For instance:
- A constructor
- Most overrides
- Properties
- Event handlers
Any method that fails to provide a Task signature is a false root. Xamarin.Forms doesn't currently provide many legal roots. They have to fabricated through programming tricks. This causes unsafe results:
public class FalselyRootedView : ContentView
{
protected override async void OnBindingContextChanged()
{
base.OnBindingContextChanged();
// Mega hack -- called from a void method (illegal!)
await StartUpViewModel().ConfigureAwait(false);
}
public virtual Task StartUpViewModel()
{
return Task.CompletedTask;
}
}
// Derive and consume the falsely rooted view as if it were valid
public class FalseConsumer : FalselyRootedView
{
pubic override async Task StartUpViewModel()
{
// Everything seems OK from this perspective, but this task can proceed at any time and
// without our control; it was never properly awaited. Anything relying on it will
// accelerate into a race condition; variables will not be set on time; nothing can
// be relied upon in a predictable order.
await SomeOtherTask();
}
}
Until Microsoft converts all current code signatures to Tasks, programmers are stuck using these sorts of risky mechanisms.
Case In Point: Event Handlers
Before you write to me and tell me that you have figured out a way around this limitation, such as:
public SomeConstructor()
{
BindingContextChanged += async (sender, args) => { await SomeMethod(); };
}
Let's face facts: this event is raised as follows:
BindingContextChanged?.Invoke(this, args);
That is an illegal root!!! It is not awaited.
Solution: Responsive Tasks
The ResponsiveTasks library is a drop-in replacement for Microsoft events. It is completely task-based. It is multi-cast, so supports any number of listeners. And it offers many other highly nuanced capabilities that far exceed the nuts-and-bolts approach of System.Events.
The Old Way -- Events
Event host:
public class MyBadHost
{
private bool _isTrue;
public event EventHandler<bool> IsTrueChanged;
public bool IsTrue
{
get => _isTrue;
set
{
_isTrue = value;
IsTrueChanged?.Invoke(this, _isTrue);
}
}
}
Event Consumer:
public class MyBadConsumer
{
public MyBadConsumer(MyBadHost host)
{
// Falsely rooted async call
host.IsTrueChanged += async (b) => await HandleIsTrueChanged();
}
private Task HandleIsTrueChanged(object sender, bool e)
{
// Do something
return Task.CompletedTask;
}
}
The New Way -- Responsive Tasks
Event host:
public class MyGoodHost
{
private bool _isTrue;
// Defaults to AwaitAllSeparately_IgnoreFailures; fully configurable
public IResponsiveTasks IsTrueChanged { get; set; } = new ResponsiveTasks(1);
public bool IsTrue
{
get => _isTrue;
set
{
_isTrue = value;
// Can still use this, though improperly rooted
// FireAndForget is a standard utility that runs a Task from a void
// signature using try/catch. It doesn't cure any ills; it just
// isolates and protects better than loose code.
SetIsTrue(_isTrue).FireAndForget();
}
}
// Properly designed for awaiting a Task
public async Task SetIsTrue(bool isTrue)
{
// The param is passed here as a simple Boolean
await IsTrueChanged.RunTaskUsingDefaults(new object[] { isTrue });
}
}
Event Consumer:
public class MyGoodConsumer
{
public MyGoodConsumer(MyGoodHost host)
{
// Subscribe to the task; not illegal
host.IsTrueChanged.AddIfNotAlreadyThere(this, HandleIsTrueChanged);
}
// Handle the task using a task
private Task HandleIsTrueChanged(IResponsiveTaskParams paramDict)
{
// Get the params formally and with type safety in the first position:
var boolParam = paramDict.GetTypeSafeValue<bool>(0);
// OR instead of this, just fuh-get-about-it:
boolParam = (bool)paramDict[0];
// Do something with the param
return Task.CompletedTask;
}
}
Responsive Tasks Features
On Creating Tasks
- Can assign any parameter count; on firing the task, the provided parameters must match that count or an error will result.
- Can run the tasks in parallel or consecutively (default)
- Can respond to errors as the tasks run or not (default)
- Can set the error level (debug output vs. modal dialog, etc.)
- Can provide a custom error handler
On Handling Tasks
- Can get the parameters with type safety (recommended)
- Can get parameters directly through array referencing (unsafe)
- Upon request to handle a hosted task, if that subscription already exists, it is ignored - NO duplicate subscriptions as with events!
- Very well-behaved storage model; subscribed tasks do not mis-behave like subscribed events do on disposal of the listening class.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Acr.UserDialogs (>= 7.1.0.483)
- Com.MarcusTS.SharedUtils (>= 1.0.18)
- Xamarin.Essentials (>= 1.6.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Com.MarcusTS.ResponsiveTasks:
Package | Downloads |
---|---|
Com.MarcusTS.PlatformIndependentShared
Platform independent utilities for C# development. |
|
Com.MarcusTS.UI.XamForms
Xamarin.Forms abstract classes and utilities to support creating flowable animated apps. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
2.0.5 | 990 | 7/30/2022 |
2.0.4 | 420 | 7/29/2022 |
2.0.3 | 1,843 | 10/29/2021 |
2.0.2 | 375 | 10/29/2021 |
2.0.1 | 625 | 10/28/2021 |
1.0.22 | 387 | 10/28/2021 |
1.0.21 | 342 | 8/27/2021 |
1.0.20 | 340 | 8/24/2021 |
1.0.19 | 360 | 8/24/2021 |
1.0.18 | 351 | 8/17/2021 |
1.0.17 | 388 | 8/17/2021 |
1.0.16 | 488 | 8/15/2021 |
1.0.15 | 415 | 8/10/2021 |
1.0.14 | 376 | 8/10/2021 |
1.0.13 | 382 | 8/10/2021 |
1.0.12 | 415 | 7/6/2021 |
1.0.11 | 396 | 7/1/2021 |
1.0.10 | 529 | 6/24/2021 |
1.0.9 | 472 | 6/2/2021 |
1.0.8 | 416 | 5/18/2021 |
1.0.7 | 336 | 5/14/2021 |
1.0.6 | 470 | 5/12/2021 |
1.0.5 | 363 | 5/12/2021 |
1.0.4 | 393 | 4/9/2021 |
1.0.3 | 381 | 4/9/2021 |
1.0.2 | 425 | 4/1/2021 |
1.0.1 | 342 | 4/1/2021 |
1.0.0 | 387 | 2/3/2021 |