Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async void when overriding

I'm aware of the evils of async void methods but am not entirely sure of the best way of over coming the problem when overriding methods.

Say, I have the following code:

protected override async void PageRefreshed()
{
    BoundDataField = await LoadDataFromWeb();
}

I know this is a really bad thing to do, but what is the best solution for this?

  1. LoadDataFromWeb().ContinueWith(r => BoundDateField = r.Result);
  2. Task.Run(async ()=> await LoadDataFromWeb())).Wait;
  3. LoadDataFromWeb().Wait
  4. BoundDataField = LoadDataFromWeb.Result

I'm pretty sure that 3 & 4 are real no nos as they will be blocking the UI thread. Is there another solution I have missed?

like image 569
Ashley Jackson Avatar asked Oct 19 '25 13:10

Ashley Jackson


1 Answers

I'm pretty sure that 3 & 4 are real no nos as they will be blocking the UI thread.

Not just blocking, but quite possibly deadlocking, too.

Is there another solution I have missed?

What you're trying to do is asynchronously retrieve the value of a data-bound property. I cover this in detail in my MSDN article on asynchronous data binding.

First, the central thing to recognize is that this is impossible, as written. There are two conflicting requirements:

  • The computer must display something immediately.
  • You need to get the data to display, and this will take time.

So, you'll need to compromise:

  • The computer gets some placeholder data or a spinner or something to display immediately.
  • You update the display with the real data when it arrives.

Put this way, the code is more straightforward:

protected override async void PageRefreshed()
{
  BoundDataField = "placeholder"; // synchronous immediate placeholder data
  BoundDataField = await LoadDataFromWeb(); // asynchronous update
}

or:

protected override async void PageRefreshed()
{
  // Synchronously show spinner
  IsBusy = true;

  // Asynchronously load data and then hide spinner
  BoundDataField = await LoadDataFromWeb();
  IsBusy = false;
}

Note that this simple solution is not handling errors well and also doesn't handle multiple "refreshing"s possibly updating that field out of order. A more advanced approach is to use something like my NotifyTask<T> type from the Nito.Mvvm.Async NuGet package:

protected override void PageRefreshed()
{
  BoundDataField = NotifyTask<TData>.Create(LoadDataFromWeb());
}

This approach requires updates to your data binding code as well; BoundDataField.Result is now the actual data value, and BoundDataField.IsNotCompleted, BoundDataField.IsFaulted, and other properties can be used to have your data binding respond to in-progress or faulted states.

like image 80
Stephen Cleary Avatar answered Oct 22 '25 02:10

Stephen Cleary



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!