Reputation: 3351
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
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
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
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);
}
https://developer.xamarin.com/guides/cross-platform/advanced/async_support_overview/
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Upvotes: -1
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