在MVVM中async/await是无用的吗?

kas*_*rhj 7 .net c#

在MVVM中,ViewModels通过INotifyPropertyChanged事件更新视图,async/await的酷炫功能似乎没有多大空间; 在调用者的捕获同步上下文上执行继续.

那么,如果是这种情况,那么谁将在现代基于UI的应用程序中实际使用async/await的功能?在这种情况下,"谁"也可以表示什么模式,例如MVC变化.

我认为以下是使用TAP的好方法

ViewModel.Age
{
  set {
    await Model.SetAge(value);
    NotifyPropertyChanged("Age");
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,在捕获的syncContext上运行此操作并没有多大帮助.实际上,我们可以将所有这些都放在模型中.

Model.Age
{
  set {
    await SetAge(value);
    NotifyPropertyChanged("Age");
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,我们真的希望syncContext不是捕获的.

Ste*_*ary 29

实际上,INotifyPropertyChanged.PropertyChanged在UI同步上下文中引发数据绑定是必需的.

async/ awaitdo强制您区分属性(表示当前状态并始终是同步的)和命令(表示操作,可以是同步或异步).物业getter和setter方法不能成为async,所以有"异步设置"您的示例代码是不是一个可行的方法.

async启用异步命令.您可以使用命令绑定来异步处理路由命令,或将async委托传递给a DelegateCommand,或使用您自己的ICommand实现.无论你采用哪种方式,最终都会得到一个async void命令事件处理程序.

一个现实的例子是让VM属性在内存中设置M属性,并SaveCommand使用一个async处理程序.让async处理程序与其他VM属性交互(SaveInProgress或者可能Busy与其他async处理程序共享)是很常见的,这样UI可以在命令正在进行时做出适当的响应(通常至少会导致CanExecute返回false).

所以你的async处理程序最终看起来像:

private async void SaveCommandExecute()
{
  try
  {
    // Set VM property; updates View appropriately.
    Busy = true;

    // Do the actual saving asynchronously.
    await Model.SaveAsync();
  }
  catch (Exception ex)
  {
    // Update the VM with error information.
    Error = ex.Message;
  }
  finally
  {
    // Let the VM know we're done.
    Busy = false;
  }
}

private void SaveCommandCanExecute()
{
  return !Busy;
}
Run Code Online (Sandbox Code Playgroud)

请注意,VM属性(ErrorBusy)在捕获的UI同步上下文中更新.


这说明了asyncMVVM 的核心概念:命令可能是async,但属性(例如Busy)总是表示当前状态.

如果您要添加async到现有的MVVM应用程序,您会发现自己有几个额外的属性指示业务,也可能还有进度更新(例如,完成百分比).根据您的应用程序,您可以同时允许多个异步操作.您需要考虑将这些信息添加到视图中的好方法; 我发现这是asyncMVVM应用程序中最具挑战性的部分.