问题
I'm using Parse as a data store for an app, and I am implementing their Facebook Login functionality. AFAIK, this Login method isn't any different than other async methods so hopefully it applies.
So there is a Login.xaml page, that has a button for "Login with Facebook", and tapping this button takes you to the FacebookLogin.xaml page which contains only the WebBrowser
control as per the linked Parse documenation. In ContentPanel.Loaded
on FacebookLogin.xaml, I can use the following code to log in:
async void FacebookLogin()
{
try
{
user = await ParseFacebookUtils.LogInAsync(fbBrowser, new[] { "user_likes", "email" });
}
catch (OperationCanceledException oce)
{
// task was cancelled, try again
//task = null;
//FacebookLogin();
}
catch (Exception ex)
{
}
}
If I actually log in, it works, and I can navigate the user to the next page. The problem happens when I let the browser control load (so the async
method is waiting), and then hit the Back button, and then come back to the Facebook Login page again.
When I do this, an OperationCancelledException
is thrown, but I'm not sure how to handle it. What I've tried:
In the
catch
for theOperationCanceledException
, set re-initialize theWebBrowser
control to a new one. This had no effect.In the same
catch
, callFacebookLogin()
again, to retry. This also didn't work.I've also tried not using
await
and returning aTask<ParseUser>
but I wasn't sure how to go about doing that either.
Is there something I can do with the cancellation exception, or use a CancellationToken
to handle this better? I just want to properly handle the cancellation so that I can re-load the Facebook Login page if the user presses "Back".
SOLUTION:
Ok, after much guidance from @JNYRanger, I have come up with a working solution. There may be a better way, but this seems to work. Here is the entire code from my FacebookLogin.xaml:
public partial class LoginFacebook : PhoneApplicationPage
{
Task<ParseUser> task;
CancellationTokenSource source;
public LoginFacebook()
{
InitializeComponent();
ContentPanel.Loaded += ContentPanel_Loaded;
}
async void ContentPanel_Loaded(object sender, RoutedEventArgs e)
{
try {
source = new CancellationTokenSource();
task = ParseFacebookUtils.LogInAsync(fbBrowser, new[] { "user_likes" }, source.Token);
await task;
// Logged in! Move on...
}
catch (Exception ex) { task = null; }
}
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
if (task != null) source.Cancel(false);
base.OnBackKeyPress(e);
}
}
So basically, when "Back" is pressed, I use the CancellationToken
to request a cancel, which throws an exception. When the exception occurs, I set task
to null. Now, when re-navigating to the FacebookLogin page (without previously logging in), the task
can be successfully recreated.
回答1:
Never have async void
methods. You cannot handle exceptions properly in them. If your async
method does not return anything use async Task
(not generic) as the return type instead.
You can then await on your returned Task
to handle the exceptions properly.
If you are going to set use CancellationTokens
make your you pass your CancellationTokenSource
's token to the async
method. You can then register this token either to a callback or continue to pass the token into the LoginAsync
method if that overload is available. I used this MSDN article to get myself familiar with the cancellation methods.
Also take a look at this article from the MSDN blog in regards to avoiding the async void
issues: Async-Await Best Practices
EDIT
Per the edit in your question I wanted to point something out. If you call
Task<ParseUser> t = FacebookLogin()
You now have a Task
object that you can do stuff with. However, if you just want the ParseUser
object and have no continuations or need to do anything else with the Task
you should use await
once again.
ParseUser p = await FacebookLogin();
Since this is in an event handler (loaded) it is the one exception where it's OK to have an async void
As to what happens when you a cancellation occurs, well that's up to you. You can close the login window, cancel other tasks/methods, etc.
来源:https://stackoverflow.com/questions/21248000/handle-cancellation-of-async-method