Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use async in an mvvmcross view model?

I have a long running process in an mvvmcross viewmodel and wish to make it async (http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx).

The async keyword is currently supported in the beta channel for Xamarin.

Below is an example of how I'm currently implementing async. The IsBusy flag ccould be bound to a UI element and display a loading message.

Is this the correct way?

public class MyModel: MvxViewModel
{
    private readonly IMyService _myService;
    private bool _isBusy;

    public bool IsBusy
    {
        get { return _isBusy; }
        set { _isBusy = value; RaisePropertyChanged(() => IsBusy); ; }
    }

    public ICommand MyCommand
    {
        get
        {
            return new MvxCommand(DoMyCommand);
        }
    }

    public MyModel(IMyService myService)
    {
        _myService = myService;
    }

    public async void DoMyCommand()
    {
        IsBusy = true;
        await Task.Factory.StartNew(() =>
            {
                _myService.LongRunningProcess();
            });
        IsBusy = false;
    }

}
like image 518
Chris Koiak Avatar asked Jun 19 '13 09:06

Chris Koiak


3 Answers

You should avoid async void. When you're dealing with ICommand, you do need to use async void, but its scope should be minimized.

This modified code exposes your action as an async Task, which is unit testable and consumable from other parts of your code:

public class MyModel: MvxViewModel {   private readonly IMyService _myService;   private bool _isBusy;    public bool IsBusy   {     get { return _isBusy; }     set { _isBusy = value; RaisePropertyChanged(() => IsBusy); ; }   }    public ICommand MyCommand   {     get     {       return new MvxCommand(async () => await DoMyCommand());     }   }    public MyModel(IMyService myService)   {     _myService = myService;   }    public async Task DoMyCommand()   {     IsBusy = true;     await Task.Run(() =>     {       _myService.LongRunningProcess();     });     IsBusy = false;   } } 

Your use of IsBusy is fine; that's one common approach in asynchronous UIs.

I did change Task.Factory.StartNew to Task.Run; Task.Run is preferred in async code for reasons described by Stephen Toub.

like image 182
Stephen Cleary Avatar answered Sep 24 '22 19:09

Stephen Cleary


MvvmCross now has MvxAsyncCommand (see GitHub commit).

So instead of doing this

public ICommand MyCommand
{
  get
  {
    return new MvxCommand(async () => await DoMyCommand());
  }
}

You can do this

public ICommand MyCommand
{
  get
  {
    return new MvxAsyncCommand(DoMyCommand);
  }
}
like image 22
Colin Bacon Avatar answered Sep 23 '22 19:09

Colin Bacon


Looks OK except I would add a try catch finally around that await.

    public async void DoMyCommand()
    {
        IsBusy = true;
        try{
            await Task.Factory.StartNew(() =>
                                        {
                _myService.LongRunningProcess();
            });
        }catch{
            //Log Exception
        }finally{
            IsBusy = false;
        }
    }

Further more I have an example on my blog using an MvxCommand with async. Very similar to your example http://deapsquatter.blogspot.com/2013/03/updating-my-mobile-apps-for-async.html

like image 29
Kevin Avatar answered Sep 20 '22 19:09

Kevin