Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to implement an async "loadData" method using the MVVM pattern

I'm trying to understand what is the best approach to use when calling an async method that updates my ViewModel. Right now, let's say I have something like this:

View:

private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
    //Call my ViewModel method to update the data the UI is bound to          
}

ViewModel:

public async Task loadData()
{
    this.Source = await loadStuffFromDatabaseAsync();
}

Now, I'm not sure which one of the following approaches should I use:

1) In my LoadState method, use:

await Task.Run(async () => { await ViewMode.loadData(); });

2) Use Task.Run without awaiting the loadData method inside the Action :

await Task.Run(() => { ViewModel.loadData(); });

3) Call my loadData method with:

await ViewModel.loadData().ConfigureAwait(false);

4) Call the loadData method without awaiting it in my View class and use Task.Run inside my loadData method:

View:

private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
    ViewModel.loadData();  
}

ViewModel:

public async void loadData()
{
    await Task.Run(async () => 
    {
        this.Source = await loadStuffFromDatabaseAsync();
    });
}

What are the main differences between these approaces?

Is one more efficient that the other, and should I pick one in particular?

Thanks for your help! :)

Sergio

like image 675
Sergio0694 Avatar asked Feb 23 '15 20:02

Sergio0694


1 Answers

You should only use Task.Run if you have CPU-bound or blocking work that you want to move off the UI thread. That's not the case here, so the direct call (option 3) is the most natural.

Taking them in turn:

await Task.Run(async () => { await ViewMode.loadData(); });

This option will execute loadData on a thread pool thread. This may not work very well, since loadData is updating the UI (indirectly by setting a VM property). Even if it does happen to work (i.e., some MVVM frameworks can properly handle updates from background threads in some scenarios), it's probably unnecessary since loadData is an asynchronous method.

Furthermore, it adds async state machine overhead for no reason.

await Task.Run(() => { ViewModel.loadData(); });

This option has all the same problems, except it's slightly more efficient since it doesn't have the async state machine overhead. But it's still updating VM properties on a background thread and using a background thread unnecessarily.

public async void loadData()

This one's the worst of all. It inherits the same problems of the others: updating VM properties on a background thread and using an unnecessary background thread. To that it adds the problems of async void. One problem is that NavigationHelper_LoadState cannot catch any exceptions from loadData. Another problem is that loadData is not easily testable.

So just use the simple approach and call it directly:

await ViewModel.loadData().ConfigureAwait(false);
like image 111
Stephen Cleary Avatar answered Nov 07 '22 11:11

Stephen Cleary