提问人:sorosh_sabz 提问时间:4/2/2017 最后编辑:sorosh_sabz 更新时间:10/29/2022 访问量:25240
如何在 DelegateCommand 中使用异步方法
How to use async method in DelegateCommand
问:
我想将异步方法链接到 Xamarin.Forms 中棱镜框架中的委托命令,我的问题是该怎么做?
以下解决方案是否正确?有什么陷阱吗?(死锁、UI 缓慢或冻结、不良做法......
{ // My view model constructor
...
MyCommand = new DelegateCommand(async () => await MyJobAsync());
...
}
private async Task MyJobAsync()
{
... // Some await calls
... // Some UI element changed such as binded Observable collections
}
答:
public ICommand MyCommand{get;set;}
//constructor
public ctor()
{
MyCommand = new Xamarin.Forms.Command(CmdDoTheJob);
}
public async void DoTheJob()
{
await TheMethod();
}
评论
public DelegateCommand MyCommand => new DelegateCommand(MyMethod);
private async void MyMethod()
{
}
没有陷阱。在异步方法中专门为委托创建了 void 返回类型。如果要更改已反映在UI上的内容,请在此块中插入相关代码:
Device.BeginOnMainThread(()=>
{
your code;
});
实际上,ICommand 和 DelegateCommand 非常相似,所以上面的答案是完全正确的。
评论
Device.BeginOnMainThread
async
如前所述,使用 delegate 命令处理异步代码的方法是使用 .关于这一点的讨论已经很多了,远远超出了 Prism 或 Xamarin Forms。底线是 Xamarin Forms 和 Prism 都受到 的限制。如果您想获得有关此内容的更多信息,我鼓励您阅读 Brian Lagunas 的博客,解释为什么 DelegateCommand.FromAsync
处理程序已过时。async void
ICommand
Command
DelegateCommand
ICommand
void Execute(object obj)
通常,通过更新代码可以很容易地处理大多数问题。例如。我经常听到抱怨为什么需要 FromAsync 作为“原因”,只是在他们的代码中看到他们从未尝试过。因为是一劳永逸,我听到的另一个抱怨是命令可以执行两次。这也很容易用 和 修复。Exceptions
async void
DelegateCommands
ObservesProperty
ObservesCanExecute
评论
IAsyncCommand
async void
async Task
UI 线程是否运行 DelegateCommand 和后台线程是否运行 await 表达式?
是的,UI 线程运行 .如果是 one,它会一直运行到第一个语句,然后恢复他的常规 UI 线程工作。如果 awaiter 配置为捕获同步上下文(即,您不使用 ),则 UI 线程将在 .DelegateCommand
async
await
.ConfigureAwait(false)
DelegateCommand
await
UI 线程是否运行 DelegateCommand 和后台线程是否运行 await 表达式?
“await 表达式”是否在后台线程、前台线程、线程池线程或其他线程上运行取决于您调用的 API。例如,您可以使用以下方法将 cpu 绑定的工作推送到线程池,或者您可以等待 i/o 操作而不使用任何线程Task.Run
Stream.ReadAsync
您可以直接使用。但是,我的经验中的一些笔记......async void
代码的结构为:启动异步操作,然后使用结果更新 UI。这对我来说意味着,最好使用 NotifyTask<T>
类型的异步数据绑定方法,而不是命令。有关其背后的设计的更多信息,请参阅我的异步 MVVM 数据绑定文章(但请注意,最新代码具有错误修复和其他增强功能)。NotifyTask<T>
如果你确实需要一个异步命令(这种情况要少得多),你可以直接使用或构建一个异步命令类型,正如我在关于异步MVVM通信的文章中所描述的那样。我也有类型来支持这一点,但这些 API 在不断变化。async void
如果您选择直接使用:async void
- 请考虑公开逻辑,或者至少可供单元测试访问。
async Task
- 不要忘记正确处理异常。就像普通一样,必须正确处理来自委托的任何异常。
DelegateTask
评论
async commands
async
我认为从同步执行的异步方法(ICommand.Execute)调用异步方法时的两个主要问题是 1) 在上一个调用仍在运行时拒绝再次执行 2) 处理异常。两者都可以通过如下所示的实现(原型)来解决。这将是 DelegateCommand 的异步替代。
public sealed class AsyncDelegateCommand : ICommand
{
private readonly Func<object, Task> func;
private readonly Action<Exception> faultHandlerAction;
private int callRunning = 0;
// Pass in the async delegate (which takes an object parameter and returns a Task)
// and a delegate which handles exceptions
public AsyncDelegateCommand(Func<object, Task> func, Action<Exception> faultHandlerAction)
{
this.func = func;
this.faultHandlerAction = faultHandlerAction;
}
public bool CanExecute(object parameter)
{
return callRunning == 0;
}
public void Execute(object parameter)
{
// Replace value of callRunning with 1 if 0, otherwise return - (if already 1).
// This ensures that there is only one running call at a time.
if (Interlocked.CompareExchange(ref callRunning, 1, 0) == 1)
{
return;
}
OnCanExecuteChanged();
func(parameter).ContinueWith((task, _) => ExecuteFinished(task), null, TaskContinuationOptions.ExecuteSynchronously);
}
private void ExecuteFinished(Task task)
{
// Replace value of callRunning with 0
Interlocked.Exchange(ref callRunning, 0);
// Call error handling if task has faulted
if (task.IsFaulted)
{
faultHandlerAction(task.Exception);
}
OnCanExecuteChanged();
}
public event EventHandler CanExecuteChanged;
private void OnCanExecuteChanged()
{
// Raising this event tells for example a button to display itself as "grayed out" while async operation is still running
var handler = CanExecuteChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
}
异步无效
我个人会不惜一切代价避免“异步无效”。无法从外部知道操作何时完成,错误处理变得棘手。关于后者,例如,编写一个从“async void”方法调用的“async Task”方法几乎需要注意其失败的 Task 是如何传播的:
public async Task SomeLogic()
{
var success = await SomeFurtherLogic();
if (!success)
{
throw new DomainException(..); // Normal thing to do
}
}
然后有人在另一天写道:
public async void CommandHandler()
{
await SomeLogic(); // Calling a method. Normal thing to do but can lead to an unobserved Task exception
}
如果您使用的是 Prism Library,请查看此链接: https://prismlibrary.com/docs/commands/commanding.html#implementing-a-task-based-delegatecommand
如果你想把一个 传递给 ,在变量声明中使用这个语法CommandParameter
DelegateCommand
DelegateCommand
public DelegateCommand<object> MyCommand { get; set; }
在 ViewModel 的构造函数中,按以下方式对其进行初始化:
MyCommand = new DelegateCommand<object>(HandleTap);
其中声明为HandleTap
private async void HandleTap(object param)
希望对你有所帮助。
上一个:为什么需要石膏?
评论
MyJobAsync
如果你要塞进去,就需要成为它自己,但除此之外,一切看起来都很好async
await
async
command