stephencleary / mvvm Goto Github PK
View Code? Open in Web Editor NEWMVVM helpers, including calculated properties and asynchronous notification tasks.
License: MIT License
MVVM helpers, including calculated properties and asynchronous notification tasks.
License: MIT License
Hi Stephen,
I tried the AsyncCommand in a Xamarin Forms project and saw strange behavior.
Problem :
I expected my canExecute delegate to be invoked but it does not.
I read the source code of AsyncCommand and then traversed up the chain in Mvvm.Core to see the implementation of WeakCanExecuteChanged. Everything looks fine.
I then copied your AsyncCommand implementation locally, renamed it but still kept the same implementation i.e. it relies on WeakCanExecuteChanged from Mvvm.Core. The behavior was still the same.
I then copied your WeakCanExecuteChanged implementation locally, renamed it and used it with AsyncCommand. Still the same behavior. Since now I could debug I can see that OnCanExecuteChanged in WeakCanExecuteChanged is never being invoked.
I then replaced the WeakCollection with a List (essentially not using WeakReferences at all) and then it seemed to work.
I know that the RelayCommand implementation of MvvmLight uses the similar technique of using WeakReference and it does work (it is what I am trying to replace in my relatively larger project with AsyncCommand after reading your excellent articles).
Any suggestions.
Regards & thanks
Kapil
Hi, Stephen! Thanks for sharing you thoughts and code on MVVM async.
I tried AsyncCommand pattern in my own project, and a (probably a newb) question arose:
..so, I bind my XAML control to MyAsyncCommand.Execution.Result (where MyAsyncCommand does some time-consuming downloading), and it works almost fine. But! At the time when new command executes, the new Execution object is created, so my bindable control isn't populated with any data until the command completes. And I'd like to have it keeping the old (previously requested) data.
As I mentioned in another post, I'm a fan of NotifyTask. Recently however I noticed, well, not exactly a problem but something unexpected and I would say somewhat inconvenient. I would suggest a change.
The overall design of NotifyTask is that you hand the task in to the constructor, and then you await TaskCompleted. Meanwhile the properties of NotifyTask, such as IsCompleted, IsFaulted, ErrorMessage should be notified by NotifyTask.
But suppose the task being handed in to Create is already faulted? There are a lot of ways this could arise. What will happen is that MonitorTaskAsync will run to completion in the constructor, including NotifyProperties. But since we are still in the constructor of NotifyTask, there are no handlers attached to PropertyChanged at that point. Then when you await TaskCompleted, it returns immediately since the Task that it refers to has already run to completion. Consequently no property notifications will ever be sent.
In effect, the properties of NotifyTask are being initialized to values different from the default value (i.e. some of them are true while the default value for bool is false) but no property notifications occur. This is, I would say, somewhat logical but not at all convenient. We can't just do this:
asyncExecution = NotifyTask.Create<TOut>( methodAsync() );
asyncExecution.PropertyChanged += AsyncExecution_PropertyChanged;
await asyncExecution.TaskCompleted;
and define a handler
private void AsyncExecution_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(NotifyTask.InnerException)) {
this.HandleException( ((NotifyTask)sender).InnerException);
}
}
Instead, in addition to doing all the above we also have to test for asyncExecution.InnerException != null after the call to Create. This basically leads to duplicating the logic of the PropertyChanged handler, which is not very desirable from a design point of view.
Also the fact that you have to do this is not very discoverable, as I discovered the hard way!
To remedy this situation my suggestion is to change MonitorTaskAsync as follows:
private async Task MonitorTaskAsync(Task task)
{
try {
if (task.IsCompleted) {
await Task.Yield();
}
await task;
} catch {
} finally {
NotifyProperties(task);
}
}
This way, MonitorTaskAsync will definitely not run to completion. Instead it will always return a meaningful Task that when awaited will do the appropriate NotifyProperties.
Note, this will give you a somewhat different flow of control from the current implementation. The Create call will now always yield while in the past it would only yield if the task was not already completed.
Just found these classes and they look interesting. However I'm a little puzzled by some specific behaviour that seems contradictory.
In AsyncCommand.ExecuteAsync
it explicitly rethrows the cancel/fault state of the original task, such that it propagates out of the ICommand
. According to #6 and #17, this change was intentional to make it more like synchronous ICommand
implementations, which is fair enough -- at first.
The thing is that if the UI is bound to the command then there is no defence against this becoming an unhandled exception.
With synchronous code, you can put the appropriate logic into your DelegateCommand
(or whatever) handler, eg. a try/catch with appropriate actions for cancel and fault.
With asynchronous code, you can do the same thing -- but now you have defeated the purpose of the Execution
property -- if you handle the exception at this point then Execution.IsFaulted
will never become true and so any UI data-binding you've done to show an error state is useless. If you don't handle the exception at that point, then Execution.IsFaulted
is correct but the exception gets rethrown somewhere that you can't catch it again. (Similarly for cancel.)
There's a little bit of a catch-22 -- if you do swallow exceptions and rely on people binding to Execution
then people might be confused if they forget to do that and the exception just "vanishes". But if you don't, then Execution
itself seems useless because it will never report anything other than success (or you get unhandled exceptions).
Perhaps the model to consider here is not ICommand
, but AsyncOperation
/BackgroundWorker
. These are async and they do swallow exceptions (by reporting them in the RunWorkerCompleted
handler and nowhere else). It seems to me that AsyncCommand.Execution
better fits that model (since Task
itself is similar).
Either that or there needs to be some way to inject some user-specified exception handling code around that await but still inside AsyncCommand.ExecuteAsync
.
I created an Mvvm.AsyncCommand and bound it as the Command of a button.
I got a null pointer exception in CanExecuteChanged_add, the _canExecuteChanged variable is null.
I am not sure how to use the class. I can set the CanExecuteChanged property to assign a WeakReference-based handler. However I don't see how to trigger CanExecute=false during execaution of the command. If I call OnCanExecuteChanged before or after the command executes then it will not accomplish the objective of setting CanExcecute=false during the execution.
The class itself does not trigger CanExecuteChanged as part of Execute, so it does not set CanExecute=false during Execute.
Looking at the derived class, the use of "new" for OnCanExecuteChanged is striking. While there are some situations where "new" is a necessary design element, in most cases it should be avoided. This is because it means that a different method will be called if a derived pointer is used vs if a base pointer to the very same object is used. That behavior is not what one usually expects -- normally there is only one version of a given method for a given object.
Steve
PS I find it confusing is that there is another AsyncCommand, with a very different implementation, in StephenCleary / AsyncEx. It would be a lot easier if each class were in just one package.
Having said that, thanks for some fantastic code!!
Hello,
In Xamarin.Forms with XAML I want to bind a Geolocation async with a label.
I read there I could use your class NotifyTask:
https://blog.stephencleary.com/2013/01/async-oop-3-properties.html
So I copied the public sealed class NotifyTask in my code but I get the error message described above.
Should I add another class. Can the sealed class NotifyTask be used alone?
Regards
Hi Stephen,
is there a chance you can put up a version of this package as release version?
We have been using Nito.AsyncEx version 4.0 for quite some time and recently updated to version 5.0. However this meant to also use this library now, as the NotifyTask got moved into this repo.
We now get build warnings while building our own release packages, because we have to reference a pre-release package.
Thanks for your great work,
Philipp
CA1001 is currently supressed.
Stephen,
Can chance you can update the library to include AsyncCommand ?
Hi Stephen,
I'm trying to use AsyncCommand, this is my example code:
public AsyncCommand TestCommand
{
get
{
if (_testCommand == null)
{
_testCommand = new AsyncCommand(
async (x) =>
{
await SaveAsync();
});
}
return _testCommand;
}
}
private async Task SaveAsync()
{
await Task.Delay(1);
throw new NotImplementedException();
}
The exception in SaveAsync isn't captured by AsyncCommand.
What is wrong with my code?
Thank you
Giampaolo
Hi Stephen,
I am using NotifyTask to load data in a viewmodel constructor in a WPF project but it is taking a few seconds for the data to show on the screen. It is the same using AsyncEx and possibly related to http://stackoverflow.com/questions/29058130/why-does-this-async-method-still-work-on-ui-thread-and-slow-navigation
My viewmodel is basically the code below and I bind the datacontext of a grid to Test.Result.
public class TestViewModel : ViewModelBase
{
private readonly ITestService _TestService;
private int _Id;
NotifyTask<TestDto> _Test;
public int Id
{
get
{
return _Id;
}
set
{
if (_Id == value)
{
return;
}
_Id = value;
RaisePropertyChanged(() => Id);
}
}
public NotifyTask<TestDto> Test
{
get
{
return _Test;
}
set
{
if (_Test == value)
{
return;
}
_Test = value;
RaisePropertyChanged(() => Test);
}
}
public TestViewModel(int id)
{
_Id = id;
_TestService = ServiceLocator.Current.GetInstance<ITestService>();
Test = NotifyTask.Create(_TestService.GetTest(_Id));
}
}
Most of the time I don't need a parameter for my commands. It would be nice to not have to create my delegates(?) to accept a parameter they'll never use.
I suggest creating a constructor overload:
// This is the current one
AsyncCommand(Func<object, Task> executeAsync, Func<object, bool> canExecute = null)
// This is my suggested overload
AsyncCommand(Func<Task> executeAsync, Func<bool> canExecute = null)
I know Xamarin.Forms has this overload. I haven't tried any other Command implementations yet.
The current version CancelCommand.Wrap()
does not propagate IsCancelled
information to the Execution.IsCancelled
of the parent AsyncCommand
.
In the following code
public AsyncCommandsDemoViewModel(IDataClient dataClient)
{
this.CancelGetData = new CancelCommand();
this.GetData = new AsyncCommand(
this.CancelGetData.Wrap(
async cancellationToken =>
this.Data = await dataClient.GetDataAsync(cancellationToken)));
}
if the CancelGetData
command is executed, GetData.Execuction.IsCancelled
never becomes true
.
The current version of the AsyncCommand
rethrows any exception that occurs in the task run by the command.
In the following code
public AsyncCommandsDemoViewModel(IDataClient dataClient)
{
this.CancelGetData = new CancelCommand();
this.GetData = new AsyncCommand(
this.CancelGetData.Wrap(
async cancellationToken =>
this.Data = await dataClient.GetDataAsync(cancellationToken)));
}
If the GetDataAsync
throws any exception it will be propagated to the UI thread causing the application to crash.
Is it possible to make Nito.Mvvm.CalculatedProperties independent, if I only use CalculatedProperties then I don't want this package to depend on Nito.Mvvm.Core.
Hi Stephen,
for unit testing purposes it would be great if NotifyTask (and its generic version) has an interface. This allows mocking of the NotifyTask.
Thanks, Felix
I have a viewmodel for a sign-in form (in Xamarin.Forms). I want the following:
Simplified, the viewmodel (which doesn't work properly) is defined like this:
// skipping INotifyPropertyChanged implementation for user/pass for brevity
public string Username { get; set; }
public string Password { get; set; }
public IAsyncCommand SignInCommand { get; set; }
public SignInPageModel()
{
SignInCommand = new AsyncCommand(SignInAsync, SignInCommandCanExecute);
PropertyChanged += delegate { ((AsyncCommand) SignInCommand).OnCanExecuteChanged(); };
}
private async Task SignInAsync(object _)
{
// actual sign-in process here
await DoSomeWork();
}
private bool SignInCommandCanExecute(object _)
{
return !string.IsNullOrEmpty(Username) &&
!string.IsNullOrEmpty(Password) &&
!((AsyncCommand SignInCommand).Executing;
}
What I'm mainly wondering is how I should change this to implement requirement 1. When the command is invoked, there is no way I can call OnCanExecuteChanged
so that SignInCommandCanExecute
returns the correct value. If I call OnCanExecuteChanged
just before and after await DoSomeWork()
(which is the earliest and latest I can do it AFAIK, at least without mucking around in the UI codebehind), then the command (and the button that binds to it) is disabled just a tad bit late, and not re-enabled at all (because it's still executing as long as SignInAsync
is running).
Unless I have missed an obvious solution, I suggest you fix this by calling OnCanExecuteChanged
during the command execution process whether or not CanExecute is set when instantiating the AsyncCommand (i.e., https://github.com/StephenCleary/Mvvm.Async/blob/master/src/Nito.Mvvm.Async/AsyncCommand.cs#L126-L127 and later in that method).
Furthermore, for requirement 2, the obvious solution is to bind the text fields' IsEnabled
to SignInCommand.Executing
. However, the command is of type IAsyncCommand
and needs to be cast to AsyncCommand
in order to access the Executing
property. Do I really have to make a ValueConverter just for this, or is there a simpler/more elegant way?
I'm trying to use pre3
and it seems that I cannot pass a simple function any more as the the second parameter for the AsyncCommand as you have suggested here: #11 (comment)
What is the expected way of using the factory in pre3
release?
Maybe I've missed something, but I can't get AsyncCommands to work with my tests. As soon as ExecuteAsync is called from a test, an InvalidOperationException is thrown.
at Nito.Mvvm.ThreadAffinity.VerifyCurrentThread()
at Nito.Mvvm.StrongCanExecuteChanged.OnCanExecuteChanged()
at Nito.Mvvm.AsyncCommand.<ExecuteAsync>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at AsyncCommandNUnit.TestClass.<Throws>d__1.MoveNext() in C:\Projects\AsyncCommandNUnit\AsyncCommandNUnit\TestClass.cs:line 24
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at NUnit.Framework.Internal.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult) in C:\src\nunit\nunit\src\NUnitFramework\framework\Internal\AsyncInvocationRegion.cs:line 113
at NUnit.Framework.Internal.Commands.TestMethodCommand.RunAsyncTestMethod(TestExecutionContext context) in C:\src\nunit\nunit\src\NUnitFramework\framework\Internal\Commands\TestMethodCommand.cs:line 96
Here's me test code:
[TestFixture]
public class TestClass
{
[Test]
public async Task Throws()
{
AsyncCommand command = new AsyncCommand(
async () => { await Task.Delay(1000); });
await command.ExecuteAsync(null);
}
[Test]
public async Task DoesNotThrow()
{
AsyncCommand command = new AsyncCommand(
() =>
{
Thread.Sleep(1000);
return Task.FromResult(0);
});
await command.ExecuteAsync(null);
}
[Test]
public async Task AlsoThrows()
{
AsyncCommand command = new AsyncCommand(
() => Task.Delay(1000));
await command.ExecuteAsync(null);
}
}
I've already tried explicitly setting Synchronization context without success. Also I'm experiencing the same behavior when executing similar code in a simple console app.
Here's my environment:
<packages>
<package id="Nito.Disposables" version="1.2.3" targetFramework="net471" />
<package id="Nito.Mvvm.Async" version="1.0.0-pre-03" targetFramework="net471" />
<package id="Nito.Mvvm.Core" version="1.2.2" targetFramework="net471" />
<package id="NUnit" version="3.10.1" targetFramework="net471" />
<package id="System.Collections.Immutable" version="1.4.0" targetFramework="net471" />
</packages>
running on Win7
I am a fan of NotifyTask because it clarifies the use of async and how async methods capture exceptions in the returned Task.
One thing I feel could be improved in NotifyTask is to recognize that not all exceptions are the same. Some exceptions indicate a system problem (e.g. file system error, network down, etc), while others indicate a program or programmer error (e.g. ArgumentException).
NotifyTask promotes the use of exceptions for user message by providing the bindable ErrorMessage property. It is not that common to use exceptions for user messages because (a) exceptions frequently are not localized or are in other ways not user-friendly and (b) user messages are considered to be "normal" rather than "exceptional" and (c) if you use exceptions for user messages then you have to have a way of distinguishing user-message exceptions from system and programmer errors.
If a system or programmer error occurs, then I want NotifyTask to propagate the exception so that I am forced to deal with the exception. On the other hand if the exception represents a user message then I want NotifyTask to capture the exception and put it into ErrorMessage so that I can show it at a convenient time / place in the user interface.
This is quite easily accomplished by adding enum NotifyTaskExceptions { RethrowAll, RethrowNone, RethrowAllButUserInformationExceptions, RethrowUserInformationExceptionsOnly } and then using it in MonitorTaskAsync to test whether the exception should be rethrown. This provides flexibility with respect to either propagating or capturing exceptions. I am attaching the revised code.
In general, however, my suggestion is that async user notifications should be managed without the use of exceptions, because this avoids the issues (a, b, c) above. The worker/async function in the Model or in a service should be able to provide a progress report and this should then result in PropertyChanged on an object in the ViewModel. One way to handle this, promoted for example in MvvmLight, is to use a Messenger service: post a message from the Model or from the service, and have the ViewModel or the View listen for and apply the message. I am not a fan of this approach because is requires creating a new infrastructure/paradigm of messaging, and because messaging does not by itself directly solve the problem of getting status information from the Model or service to a PropertyChanged event. In addition this approach creates the problem of how to ensure that messages notifying the start of work are always matched by messages notifying the end of work.
My suggestion in this regard is to have a ProgressReport class which is handed in to the worker/async function, much in the same way that a CancellationToken is handed in. ProgressReport should implement INotifyPropertyChanged. By updating the ProgressReport, the worker/async function provides status information, which is then translated into PropertyChanged events by the ProgressReport. Obviously one has to look out for thread safety and for the fact that the PropertyChanged notification may need to be marshalled onto the UI thread. I am attaching a ProgressReport implementation which does this. The fields in the ProgressReport are designed to be convenient for the ProgressBar and ProgressRing UI controls. There is also an Activate function designed to be used in a "using" block so that the worker/async function definitely notifies when work is complete.
ProgressReport uses a NotifyBase class which takes care of marshalling the PropertyChanged onto the UI thread. There is also an additional Property class in there which lets you use lambda notation for property names -- so that the compiler checks the property names -- I got that part off a blog somewhere, unfortunately I forget the reference.
I am aware of IProgress, however this is too limited to address the issues above and also is not well suited to ProgressRing and ProgressBar.
I am hereby entering the attached code into the public domain for use by anyone without restriction.
NotifyTask.txt
ProgressReport.txt
NotifyBase.txt
Property.txt
Hmmm, forgot this:
public partial class UserInformationException: Exception
{
public UserInformationException() { }
public UserInformationException(string message) : base(message) { }
public UserInformationException(string message, Exception innerException) : base(message, innerException) { }
}
}
Not sure why this is happening, but I can't figure it out.
My command is defined like this:
public AsyncCommand ToggleCheckedCommand { get; }
// constructor
public MyViewModel()
{
ToggleCheckedCommand = new AsyncCommand(ToggleCheckedAsync);
}
private async Task ToggleCheckedAsync(object _)
{
var response = await ServerApi.DoWebRequestAsync();
if (response.Success)
...
}
If I debug the app and break at await ServerApi.DoWebRequestAsync()
, and mouseover ToggleCheckedCommand, Execution
is null and IsExecuting
is False. If I perform the web request and continue to the next line, if (response.Success)
, Execution and IsExecuting assume the expected values.
If I unit test my viewmodel and mock ServerApi
, so that it returns instantly, Execution
is null and IsExecuting
is False throughout the test (as seen when debugging and breaking at the same lines). This makes the test fail, since I'm checking for whether or not the command is disabled during execution.
Is this something you can reproduce? Using v1.0.0-eta-02.
Hi Stephen,
Firstly, thanks for all of the helpful resources you have produced over the years regarding how to best incorporate asynchronous programming into WPF/MVVM applications. They have been extremely helpful, especially as the combination of WPF, MVVM, and async/await seems to be something that there is no "perfect" solution for.
Next, apologies if creating an issue wasn't the right thing to do here, as all I really have is a quick question regarding the status of this NuGet package:
I am mainly interested in using the NotifyTask type (basically for the same reason as this question) but was just wondering if there is a specific reason why you haven't moved this package out of its "prerelease" state yet? Should I take that to mean it's not "prod ready"?
Thanks again,
Adam
Hi Stephen,
thanks for this great library! Currently Nito.Mvvm.Async is only available with version 1.0.0.pre04, with no updates in the last years. Is it possible to provide a stable version from it? Or are the functionalities provided with another package?
Thanks in advance!
For when you have a delegate that only has a cancellation token:
public Func<Task> WrapDelegate(Func<CancellationToken, Task> executeAsync)
{
return async () =>
{
using (StartOperation())
await executeAsync(CancellationToken);
};
}
I've implemented an extension method for my own purposes, but thought it would be nice for it to be in the CancelCommand class.
Hi Stephen,
This really is not an issue it's just a discussion about usage of AsyncCommand in general.
I really loved your MSDN magazine articles about this subject. They are awesome. You really explained your thought process while creating NotifyTask and AsyncCommand classes. Problem with this version on GitHub is considerable difference than your last explained version. I would really love to see your thought process while designing this class.
Another issue is cancellation. I am not sure what would be the proper way of "connecting" Async and Cancel commands. I did tried to use something like this in ViewModel constructor (same example like you used in your MSDN article):
CancelCountCommand = new CancelCommand();
CountUrlBytesCommand = new AsyncCommand.v2.AsyncCommand(async (param) => ByteCount = await TestService.DownloadAndCountBytesAsync(Url, CancelCountCommand.CancellationToken));
But the problem with this solution is that Cancel button is enabled by default. Canceling works only first time and if user tries to start AsyncCommand again, it fails saying that Task is canceled. How should I use it properly?
Thank you,
Milos
I get an unhandled TaskCancellationException when cancelling a Command.
I believe you need to add a try catch block to WrapDelegate:
public static Func<object, Task> WrapDelegate(Func<object, CancellationToken, Task> executeAsync)
{
return async () =>
{
using (StartOperation())
{
try
{
await executeAsync(CancellationToken);
}
catch
{
}
}
};
}
I finally have a good repro for this problem. If you take the code you published for the MSDN article, everything works correctly. If you then remove the code duplicated by this library and add the prerelease NuGet reference to this library then task based exceptions make it all the way up to unhandled exceptions.
AsyncCommands3.zip
Let me know if there are any questions
AsyncCommand.ExecuteAsync checks if _canExecute is null, but I believe it means to check if it is not null:
public override async Task ExecuteAsync(object parameter)
{
Execution = NotifyTask.Create(_executeAsync(parameter));
if (_canExecute != null)
base.OnCanExecuteChanged();
var propertyChanged = PropertyChanged;
propertyChanged?.Invoke(this, PropertyChangedEventArgsCache.Instance.Get("Execution"));
propertyChanged?.Invoke(this, PropertyChangedEventArgsCache.Instance.Get("IsExecuting"));
await Execution.TaskCompleted;
if (_canExecute != null)
base.OnCanExecuteChanged();
PropertyChanged?.Invoke(this, PropertyChangedEventArgsCache.Instance.Get("IsExecuting"));
await Execution.Task;
}
This isn't really an "issue" more like a question. I did not see a category for general discussion so I am posting it here. I hope that is ok.
I notice you use the PropertyChangedEventArgsCache
, when raising PropertyChanged
event. I've come across this lazy-caching-the-args approach elsewhere in implementations of INotifyPropertyChanged
. My understanding is that this is to avoid slamming the garbage collector with too many allocations when the property changes really fly. Is my understanding correct?
Because using this also means that you must lock the the global cache lock every single time you want to raise aPropertyChanged
for any client that uses it. Is that really better? Has anyone ever done any performance testing on this?
If you know your class might raise many PropertyChanged
events in a short enough time span to have an effect on performance, then why not simply have static, readonly instances of PropertyChangedEventArgs
for those properties inside their owning classes?
While unit testing a command defined as AsyncCommand
instead of IAsyncCommand
(as you mentioned in #11), I discovered that I could not access MyAsyncCommand.CanExecute
or .CanExecuteChanged
; I had to cast it everywhere I wanted to access these properties:
((IAsyncCommand) MyAsyncCommand).CanExecute
.
Just checking to see if this is a deliberate choice on your part, and if so, what the reasons are.
Incorporate feedback.
Hi Stephen,
Is there any implementation of Mvvm.Async targeting .net 4.0?
Curently I am using IAsyncCommand with WPF 4.0 from som implementation I read on your blog about this subject.
However, I am experiencing some ArgumentNullExceptions (Parameter name: task at System.Runtime.CompilerServices.AsyncServices.b__0(Object state) exceptions when the command execution async method returns right away, like this:
`private async Task SaveAsync(CancellationToken token)
{
//Do some validation and decide to return prior to continue method execution
return;
//If OK, do async stuff, returning a task
}`
As a workaround, if the code await TaskEx.Delay(1) no exception is thrown.
Thank you,
Igor.
I have done a bit of work relating to AsyncCommand which I am hoping the community can benefit from.
I had the following objectives and approaches:
I am providing source code that accomplishes all this, and I am hereby entering it into the public domain for anyone to use without restriction. Please also refer to my other post discussing NotifyTask and ProgressReport.
sjb
AsyncCommand.txt
BoundFunction.txt
BoundAction.txt
Property.txt
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.