问题描述
我使用 Parse 作为应用程序的数据存储,我正在实施他们的 Facebook 登录 功能.AFAIK,此登录方法与其他异步方法没有任何不同,因此希望它适用.
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.
所以有一个 Login.xaml 页面,它有一个用 Facebook 登录"的按钮,点击这个按钮会带你到 FacebookLogin.xaml 页面,其中只包含 WebBrowser
控件链接的解析文档.在 FacebookLogin.xaml 上的 ContentPanel.Loaded
中,我可以使用以下代码登录:
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)
{
}
}
如果我真的登录了,它就可以工作,我可以将用户导航到下一页.当我让浏览器控件加载(所以 async
方法是 waiting),然后点击 Back 按钮时,就会出现问题,然后再次返回 Facebook 登录页面.
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.
当我这样做时,会抛出一个 OperationCancelledException
,但我不确定如何处理它.我试过的:
When I do this, an OperationCancelledException
is thrown, but I'm not sure how to handle it. What I've tried:
在
OperationCanceledException
的catch
中,将WebBrowser
控件重新初始化设置为一个新控件.这没有效果.
In the
catch
for theOperationCanceledException
, set re-initialize theWebBrowser
control to a new one. This had no effect.
在同一个catch
中,再次调用FacebookLogin()
,重试.这也没有奏效.
In the same catch
, call FacebookLogin()
again, to retry. This also didn't work.
我也试过不使用 await
并返回 Task
,但我也不知道如何去做.
I've also tried not using await
and returning a Task<ParseUser>
but I wasn't sure how to go about doing that either.
我可以对取消异常做些什么,或者使用 CancellationToken
来更好地处理这个问题?我只想正确处理取消,以便在用户按下返回"时我可以重新加载 Facebook 登录页面.
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".
解决方案:
好的,经过@JNYRanger 的大量指导,我想出了一个可行的解决方案.可能有更好的方法,但这似乎有效.这是我的 FacebookLogin.xaml 中的完整代码:
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);
}
}
所以基本上,当按下返回"时,我使用 CancellationToken
来请求取消,这会引发异常.当异常发生时,我将 task
设置为 null.现在,当重新导航到 FacebookLogin 页面(之前没有登录)时,可以成功重新创建 task
.
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.
推荐答案
从来没有 async void
方法.您无法在其中正确处理异常.如果您的 async
方法没有返回任何内容,请改用 async Task
(非通用)作为返回类型.
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.
然后您可以等待返回的 Task
以正确处理异常.
You can then await on your returned Task
to handle the exceptions properly.
如果您要设置使用 CancellationTokens
使您将 CancellationTokenSource
的令牌传递给 async
方法.然后,您可以将此令牌注册到回调或继续将令牌传递到 LoginAsync
方法(如果该重载可用).我使用了这篇 MSDN 文章 来获得我自己熟悉取消方法.
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.
另请参阅 MSDN 博客中有关避免 async void
问题的这篇文章:异步等待最佳实践
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()
您现在有一个 Task
对象,您可以用它来做任何事情.但是,如果您只想要 ParseUser
对象并且没有延续或需要对 Task
执行任何其他操作,您应该再次使用 await
.
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();
由于这是在事件处理程序(已加载)中,因此可以使用 async void
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.
这篇关于处理异步方法的取消的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!