Betty Crokker
Betty Crokker

Reputation: 3351

C# (Xamarin) "async void" in framework override

I have a screen that retrieves information from a remote server and displays that information to the user. I would like this information to be updated when the screen is displayed (requiring no additional user interaction).

public partial class MyPage : ContentPage
{
    protected override async void OnAppearing()
    {
        base.OnAppearing();
        try
        {
            MyLabel.Text = (await GoGetTheData ()).Stuff;
        }
        catch (Exception e)
        {
            MyLabel.Text = "Narg";
        }
    }
}

Is this acceptable? I've seen all the discussions about the evils of "async void", and how it should only be used in event handlers, and this isn't quite an event handler.

Is the framework guaranteed to be OK in this case, since I (1) called base.OnAppearing() before getting all asynchronous, and (2) caught my exceptions?

Upvotes: 4

Views: 3179

Answers (4)

Brandon Kramer
Brandon Kramer

Reputation: 1118

Assuming that this is an override of the protected method that calls the Appearing event for the page, then yes this is fine.

This method effectively is an event, it is the method that is called right before the event is raised.

EDIT: As @apineda point out in comments, the below is not actually the case.

Just note that your async stuff will be done after any event handlers for Appearing have run, since those are invoked in base.OnAppearing().

Upvotes: 1

Jon Douglas
Jon Douglas

Reputation: 13176

This is acceptable. Support for async on these framework overrides were added for a reason. There are evils of "async void" in general, but this case doesn't apply.

One note however is that you should ensure that your OnAppearing(Forms), OnCreate(Android), and ViewDidLoad(iOS) methods return as soon as possible so your users have a pleasant experience.

In fact you will see this exact syntax in plenty of Forms samples: https://github.com/xamarin/xamarin-forms-samples/search?utf8=%E2%9C%93&q=async+void+OnAppearing

Upvotes: 12

Terrance
Terrance

Reputation: 11872

In cases where you are dealing with events (OnAppear essentiallt the callback for Appear) it's totally fine.

Return Types An async method should return a Task, Task or void.

Specify the Task return type if the method does not return any other value.

Specify Task if the method needs to return a value, where TResult is the type being returned (such as an int, for example).

The void return type is used mainly for event handlers which require it. Code that calls void-returning asynchronous methods can’t await on the result.

In short use task when possible but in cases related to event handlers just use void.

If you were needing to call async otherwise then you could use a TaskCompletionSource as per this question

Is it possible to await an event instead of another async method?

private TaskCompletionSource<object> continueClicked;

private async void Button_Click_1(object sender, RoutedEventArgs e) 
{
  // Note: You probably want to disable this button while "in progress" so the
  //  user can't click it twice.
  await GetResults();
  // And re-enable the button here, possibly in a finally block.
}

private async Task GetResults()
{ 
  // Do lot of complex stuff that takes a long time
  // (e.g. contact some web services)

  // Wait for the user to click Continue.
  continueClicked = new TaskCompletionSource<object>();
  buttonContinue.Visibility = Visibility.Visible;
  await continueClicked.Task;
  buttonContinue.Visibility = Visibility.Collapsed;

  // More work...
}

private void buttonContinue_Click(object sender, RoutedEventArgs e)
{
  if (continueClicked != null)
    continueClicked.TrySetResult(null);
}

References

https://developer.xamarin.com/guides/cross-platform/advanced/async_support_overview/

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

https://developer.xamarin.com/guides/ios/user_interface/controls/part_2_-_working_with_the_ui_thread/

Upvotes: -1

Alessandro Caliaro
Alessandro Caliaro

Reputation: 5768

I use this way

protected override OnAppearing(){
    base.OnAppearing();

    Task.Run(async()=>{
        try
        {
            // Use VM and Binding... 
            MyVM.Data = (await GoGetTheData ()).Stuff;
        }
        catch (Exception e)
        {

            // Here I think you should use Device.BeginInvokeOnMainThread();
            MyVm.Data = "Narg";
        }
    });
}

and somewhere...

MyText.BindingContext = MyVm;
MyText.SetBinding(Label.TextProperty, "Data");

Upvotes: -3

Related Questions