Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is async/await useless in MVVM? [closed]

Tags:

c#

.net

In MVVM where the ViewModels update the Views via INotifyPropertyChanged events, there doesn't seem to be much room for the cool feature of async/await; executing the continuation on the captured synchronization context of the invoker.

So, if this is the case, then who will actually use the features of async/await in a modern UI based app? In this context, "who" can also mean what pattern, e.g. MVC-variation.

I can think of the following as a good way of using TAP

ViewModel.Age
{
  set {
    await Model.SetAge(value);
    NotifyPropertyChanged("Age");
  }
}

however, having this run on the captured syncContext, does not really help out much. Actually, we could put all this in the model instead.

Model.Age
{
  set {
    await SetAge(value);
    NotifyPropertyChanged("Age");
  }
}

and now, we would really like the syncContext NOT to be the captured one.

like image 856
kasperhj Avatar asked Nov 09 '12 08:11

kasperhj


1 Answers

Actually, having INotifyPropertyChanged.PropertyChanged raised in the UI synchronization context is required for data binding.

async / await do force you to make a distinction between properties (which represent the current state and are always synchronous) and commands (which represent actions and may be synchronous or asynchronous). Property getters and setters cannot be async, so your example code with an "async set" is not a possible approach.

async enables asynchronous commands. You can use command binding to asynchronously handle routed commands, or pass an async delegate to a DelegateCommand, or use your own ICommand implementation. Whichever way you'll do it, you end up with an async void command event handler.

A realistic example is to have the VM properties set the M properties in-memory, and have a SaveCommand with an async handler. It's common to have async handlers interact with an additional VM property (SaveInProgress or maybe a common Busy shared with other async handlers) so that the UI can respond appropriately when the command is in progress (usually at least causing CanExecute to return false).

So your async handler ends up looking something like:

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;
}

Note that the VM properties (Error and Busy) are updated in the captured UI synchronization context.


This illustrates the central concept of async MVVM: commands may be async, but the properties (such as Busy) always represent the current state.

If you're adding async to an existing MVVM app, you'll find yourself with several additional properties indicating business and possibly also progress updates (e.g., percent complete). Depending on your application, you may permit multiple asynchronous operations at the same time. You'll need to think of good ways to add this information to your Views; I find this to be the most challenging part of async MVVM apps.

like image 96
Stephen Cleary Avatar answered Nov 02 '22 12:11

Stephen Cleary