本文介绍了处理异步方法的取消的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 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:

  1. OperationCanceledExceptioncatch 中,将WebBrowser 控件重新初始化设置为一个新控件.这没有效果.

  1. In the catch for the OperationCanceledException, set re-initialize the WebBrowser 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.

这篇关于处理异步方法的取消的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-21 11:19