Xamarin 如何在DelegateCommand中使用异步方法
我想将异步方法链接到Xamarin.Forms中prism框架中的委托命令,我的问题是如何做 下面的解决方案正确吗?有什么陷阱吗?(死锁、UI缓慢或冻结、不良做法等) 没有陷阱。异步方法中的void返回类型是专门为委托创建的。如果要更改反映在UI上的内容,请在此块中插入相关代码:Xamarin 如何在DelegateCommand中使用异步方法,xamarin,mvvm,xamarin.forms,async-await,prism,Xamarin,Mvvm,Xamarin.forms,Async Await,Prism,我想将异步方法链接到Xamarin.Forms中prism框架中的委托命令,我的问题是如何做 下面的解决方案正确吗?有什么陷阱吗?(死锁、UI缓慢或冻结、不良做法等) 没有陷阱。异步方法中的void返回类型是专门为委托创建的。如果要更改反映在UI上的内容,请在此块中插入相关代码: Device.BeginOnMainThread(()=> { your code; }); 实际上,ICommand和DelegateCommand非常相似,所以上面的答案非常正确 如前所述,使用委托
Device.BeginOnMainThread(()=>
{
your code;
});
实际上,ICommand和DelegateCommand非常相似,所以上面的答案非常正确 如前所述,使用委托命令处理异步代码的方法是使用
async void
。关于这一点,已经有很多讨论,远远超出了棱镜或Xamarin形式。底线是,ICommand
Xamarin表单命令
和PrismDelegateCommand
都受到ICommand
的无效执行(对象obj)
的限制。如果你想得到更多关于这方面的信息,我鼓励你阅读Brian Lagunas的博客,解释原因
通常,通过更新代码可以很容易地处理大多数问题。例如我经常听到人们抱怨异常
是FromAsync之所以必要的“原因”,只是在他们的代码中他们从来没有尝试过。因为async void
是fire and forget,所以我听到的另一个抱怨是一个命令可能执行两次。这也可以通过DelegateCommands
ObservesProperty
和ObservesCanExecute
轻松解决
UI线程是否运行DelegateCommand,后台线程是否运行Wait表达式
是的,UI线程运行DelegateCommand
。对于async
one,它运行到第一个wait
语句,然后恢复其常规UI线程工作。如果将等待程序配置为捕获同步上下文(即,您不使用.ConfigureAwait(false)
),则UI线程将在await
之后继续运行DelegateCommand
UI线程是否运行DelegateCommand,后台线程是否运行Wait表达式
“等待表达式”是否在后台线程、前台线程、线程池线程或其他线程上运行取决于您调用的api。例如,您可以使用Task.Run
将cpu绑定的工作推送到线程池,也可以使用Stream等方法等待i/o操作,而不使用任何线程。ReadAsync
您可以直接使用异步void
。然而,从我的经验中可以看出
代码的结构是:启动异步操作,然后用结果更新UI。这对我来说意味着,使用一种异步数据绑定的方法,而不是命令,会更好。有关NotifyTask
背后的设计的更多信息,请参见my(但请注意,具有错误修复和其他增强功能)
如果您确实需要一个异步命令(这是非常罕见的),您可以直接使用async void
,或者构建一个异步命令类型,就像我在上一篇文章中描述的那样。我也有,但这些API的变化更大
如果您选择直接使用async void
:
- 考虑将
逻辑公开,或者至少让单元测试可以访问异步任务
- 不要忘记正确处理异常。就像普通的
一样,必须正确处理来自委托的任何异常DelegateTask
公共密封类AsyncDelegateCommand:ICommand
{
私有只读函数Func;
私有只读操作容错;
private int callRunning=0;
//传入异步委托(接受对象参数并返回任务)
//以及处理异常的委托
公共AsyncDelegateCommand(Func-Func,Action FaultHandleration)
{
this.func=func;
this.faultHandleration=faultHandleration;
}
公共布尔CanExecute(对象参数)
{
返回callRunning==0;
}
public void Execute(对象参数)
{
//如果为0,则将callRunning的值替换为1,否则返回-(如果已为1)。
//这样可以确保一次只有一个正在运行的调用。
if(联锁的比较交换(ref callRunning,1,0)==1)
{
返回;
}
OnCanExecuteChanged();
func(parameter).ContinueWith((task,)=>ExecuteFinished(task),null,TaskContinuationOptions.ExecuteSynchronously);
}
私有void ExecuteFinished(任务)
{
//将callRunning的值替换为0
联锁交换(参考callRunning,0);
//任务出现故障时调用错误处理
if(task.IsFaulted)
{
FaultHandleration(任务异常);
}
OnCanExecuteChanged();
}
公共事件处理程序CanExecuteChanged;
私有void OnCanExecuteChanged()
{
//例如,引发此事件会告诉按钮在异步操作仍在运行时显示为“灰色”
var handler=CanExecuteChanged;
if(handler!=null)handler(this,EventArgs.Empty);
}
}
异步无效
我个人会不惜一切代价避免“异步无效”。不可能从外部知道操作何时完成,错误处理变得棘手。对于后者,例如,编写一个“异步任务”方法,该方法是从一个“异步void”方法调用的
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()
{
}
Device.BeginOnMainThread(()=>
{
your code;
});
MyCommand = new DelegateCommand(() => { MyJobASync()});
private async Task MyJobASync()
{
// your method
}
public DelegateCommand<object> MyCommand { get; set; }
MyCommand = new DelegateCommand<object>(HandleTap);
private async void HandleTap(object param)