When is the Usage of ConfigureAwait(false) in ViewModels problematic?

1k Views Asked by At

I wonder in which situations I will run into problems when using

ConfigureAwait(false)

in my (Xamarin) MVVM approach. This is mainly because I do not fully understand the synchronization context a view and a viewmodel and its properties and the underlaying model have to each other...

1 What about observable collections?

// VM
public ObservableCollection<SomeThing> SomeThings { get; set; }
// ...
public Task InitWorkload()
{
    SomeThings = await DbService.GetSomeThings(); // <-- Should need synchronization context, doesn't it?
}

// Service
public Task<SomeThings> GetSomeThings()
{
    result = await CallToDb.ConfigureAwait(false); // <-- This is UI agnostic and shouldn't care about context or does it?
    return result;
}

2 What about Navigation (in this case with the help of FreshMvvm)?

private async Task CloseWindow()
{
    await CoreMethods.PopPageModel(); // <-- Should need synchronization context, doesn't it?
}
1

There are 1 best solutions below

2
On

To answer the title question, "When is the Usage of ConfigureAwait(false) in ViewModels problematic?" is "Never." Its use in a view model is irrelevant. What matters is what thread you want to run on after the async method is called, whether or not you are in a view model is irrelevant. The only time that using ConfigureAwait(false) may be problematic is if you want to return to the thread that was running before the async method was called.

And for reference, docs on the SynchronizationContext class.

Maybe explaining what ConfigureAwait(false) does is the best approach to answer this. When one calls an async method like so:

var x = await SomeMethodAsync();
var y = x;

The code var y = x will run on the same thread that the work was being done on before the async method was called, i.e the Synchronization context prior to the async method call, however if you use ConfigureAwait(false), e.g.:

var x = await SomeMethodAsync().ConfigureAwait(false);
var y = x;

then the code var y = x will run on the same thread that the SomeMethodAsync method was running on when it returned. (Presumably SomeMethodAsync uses Task.Run or Thread.StartNew which are the main ways of launching a new thread... await does not start a new thread, it is only syntactic sugar to allow your async code to be more readable so that the code following the async method call does not have to be an a delegate method or lambda, but can be in-line just like synchronous code.)

So what you need depends on what might need to be updated. Any UI updates need to run on the Main or UI thread. You can always marshal code to the Main or UI thread with Device.BeginInvokeOnMainThread(Action).

Generally, when you are in a, let's say, button click handler event, the event handler method starts running on the UI thread. If you need to call one async method and then update some UI, then do not use ConfigureAwait(false) so that after the async method you return to the Main/UI thread and you can update your UI. If you do not need to update any UI, then feel free to call ConfigureAwait(false) so that you do not unnecessarily return to the UI thread when there is no need to be running on that thread.

In some cases if you are calling multiple async methods from one method that starts on the UI thread, you might want to use ConfigureAwait(false) such that you are not going back and forth to the UI thread from background threads multiple times, which cause performance issues, but rather all of the Non-UI work is done on a background thread, and then you just manually marshal back to the UI thread when and if needed.

As for an ObservableCollection, it does not have thread affinity, i.e. when an object can only be updated or modified on the same thread that it was created on, but it is also not thread safe, so your best plan is to only access or change the ObservableCollection on the same thread that it was created on, most likely he Main/UI thread.