温故而知新系列--Windows 8 的异步编程(二)可爱的task和then(await)

温故而知新系列--Windows 8 的异步编程(二)可爱的task和then(await)
     这是Windows 8 异步编程的第二部分,为什么分成两节呢,因为,第二节确实太让人兴奋了。恰巧有篇Windows 8 开发人员博客也是这样一个结构,题目是WinRT 和 await,之前看过,没有什么感觉,今天试了一下,里面还是有很多需要注意的地方。

参考文献:
      1. Windows 8 应用程序开发人员博客:深入探究WinRT和await: http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/04/30/winrt-await.aspx 
      2. MSDN

因为我使用的是C++语言,所以我主要是用C++来编程,C#开发的朋友可以看参考文献1,以下是我的理解和我觉得重要的东西。请原谅我有一部分内容是摘抄自文献1.
一。 基础知识
      我也觉得应该从基础着手,WinRT中的所有异步功能全部源自一个接口:Windows::Foundation::IAsyncInfo

1  public  interface IAsyncInfo
2 {
3     AsyncStatus Status {  get; } // 只读
4      HResult ErrorCode {  get; } // 只读
5       uint Id {  get; } // 只读
6 
7      void Cancel();
8      void Close();
9 }
      所有的异步操作都应该实现此接口。但是该接口缺少了一个至关重要的功能:操作完成之后的回调函数。因此就有了下面四个接口。我想说,请大家注意每个新街口中都有一个Completed的属性,我们可以设置这个属性所要执行的代码,就是我们的操作完成之后,将要执行的代码。

1
  public   interface  IAsyncAction : IAsyncInfo
 2 {
 3     AsyncActionCompletedHandler Completed {  getset; }
 4      void GetResults(); //  void,不返回结果
 5  }
 6 
 7  public  interface IAsyncOperation<TResult> : IAsyncInfo
 8 {
 9     AsyncOperationCompletedHandler<TResult> Completed {  getset; }
10     TResult GetResults(); // TResult 返回结果
11  }
12 
13  public  interface IAsyncActionWithProgress<TProgress> : IAsyncInfo
14 {
15     AsyncActionWithProgressCompletedHandler<TProgress> Completed {  getset; }
16     AsyncActionProgressHandler<TProgress> Progress {  getset; } // 有Progress
17       void GetResults(); // void,不返回结果
18  }
19 
20  public  interface IAsyncOperationWithProgress<TResult, TProgress> : IAsyncInfo
21 {
22     AsyncOperationWithProgressCompletedHandler<TResult, TProgress> Completed {  getset; }
23     AsyncOperationProgressHandler<TResult, TProgress> Progress {  getset; } // 有Progress
24      TResult GetResults(); // TResult,返回结果
25  }
    
        暂停,我想说,C#开发人员真的很幸运,C++开发人员需要写很久的代码,C#只要一点点就足够了。这是我的牢骚,请忽略。
      那么我们还是以打开文件为例吧,上一篇文章中,我们使用了create_task这个方法,打开了文件,并且将一些内容显示在一个OutputTextBlock中,现在我们不使用task类,只使用我们的IAsyncOperation来做。
      好,下面是C++同学的时间,请问,你能写出completed这个event的代码么?并保证其运行。

那开始吧。要知道FileOpenPicker::PickSingleFileAsync()返回的是一个Windows::Foundation::IAsyncOperation<Windows::Storage::StorageFile^>^类型的操作:
Windows::Foundation::IAsyncOperation<Windows::Storage::StorageFile^>^ operation = openPicker->PickSingleFileAsync(); // 为了让大家看清楚类型,就没有用auto简写,大家用auto的话会非常简单
      对应上面的四个接口,显然,返回的是第二个接口IAsyncOperation<T TResult>^操作,那么它有一个Windows::Foundation::AsyncOperationCompletedHandler<TResult>^ 类型的Completed事件,那么我们的completed就应该写成这样:
operation->Completed =  ref  new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>( // 暂时省略);
      好复杂!
      还没完,继续。AsyncOperationCompletedHandler是一个Delegate,这个Delegate其实就是原来的函数指针封装了一下,既然是函数指针,那么肯定有参数,怎么寻找这个参数呢?把鼠标移到这个方法上,按下F12就会谈到Object Browse里面去,你就会发现这个Delegate,它有一个Invoke(。。。)方法,里面就是它的参数了。
      另外,我们没使用+=操作符,而是使用了=,为什么呢?因为Completed是个属性,不是个event。。。
      回调函数的代码段,我们用Lambda表达式来写:
        1   operation -> Completed  =   ref   new  Windows::Foundation::AsyncOperationCompletedHandler < Windows::Storage::StorageFile ^> ([ this ](Windows::Foundation::IAsyncOperation < Windows::Storage::StorageFile ^>^  asyncInfo, Windows::Foundation::AsyncStatus status) 
 2         {
 3              // 这里,貌似程序会在这里执行3次,具体原理不太清楚。
 4               if(status == Windows::Foundation::AsyncStatus::Completed)
 5             {
 6                  // 因为执行3次,所以我在这个里面加了一个计数器,但是最终的结果还是1,这个异步方法还是执行了一次
 7                   // 这里为什么是要Dispatcher->RunAsync呢,因为,这是一个回调函数,另一个线程中了。
 8                   // 可以看到,这所有的一切都可以用then或者await来代替。
 9                   this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,  ref  new Windows::UI::Core::DispatchedHandler([ this](){
10                      static  int i = 0;
11                     i++;
12                     OutputTextBlock->Text = i.ToString();
13                 }));
14             }
15              // 如果执行了operation->Canceled()方法会到这里
16               else  if(status == Windows::Foundation::AsyncStatus::Canceled)
17             {
18                  this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,  ref  new Windows::UI::Core::DispatchedHandler([ this](){
19                     OutputTextBlock->Text = "Canceled";
20                 }));
21             }
22              // 操作出现错误会到这里
23               else  if(status == Windows::Foundation::AsyncStatus::Error)
24             {
25                  this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,  ref  new Windows::UI::Core::DispatchedHandler([ this](){
26                     OutputTextBlock->Text = "Error";
27                 }));
28             }
29              // 这里不能返回StorageFile^ 类型的对象,为神马呢?因为TResult是void,如果你返回了,那么编译器会提醒你很长一段东西,你自己就会发现了
30               // return safe_cast<Windows::Storage::StorageFile^>(asyncInfo->GetResults());
31          });

      具体的内容都是上面的这段代码了,有些东西我想再提醒一下。
1. 首先,我们来看第30行,因为我觉得这个方法是返回一个StorageFile对象的,所以我在Lambda方法中理应返回它,但是如果返回的话,就是错误的,为什么?原因是这里的返回值实际上是Invoke方法的返回值,在这里这个返回值是void的,所以你不能返回任何东西。
2. 使用asyncInfo->GetResults();可以得到你的结果,回头看看那四个接口,有些返回TResult,有些返回void,是吧。
3. 在这个代码段里,我们已经跑到了另外一个线程中去了,如果你想操作UI,那么,你必须使用Dispatcher->RunAsync方法调度回UI线程,不信你可以试试。
4. 你可以在代码中判断现在IAsyncOperation的状态。


       哦,看看C#程序员的优越性把:
op.Completed = (info, status) =>
            {
                 if (status == AsyncStatus.Completed)
                {
                    SyndicationFeed feed = info.GetResults();
                    UpdateAppWithFeed(feed);
                }
                 else  if (status == AsyncStatus.Canceled)
                {
                     //  Operation canceled
                }
                 else  if (status == AsyncStatus.Error)
                {
                     //  Error occurred, Report error
                }
            };
       
希望你还能坚持。*^◎^*
       看到这里你发现了么?这段代码的功能其实是跟上一篇代码的功能一模一样,再把之前的代码贴过来
 1 create_task(openPicker->PickSingleFileAsync()).then([ this](StorageFile^ file)
 2         {
 3              if (file)
 4             {
 5                 OutputTextBlock->Text = "Picked photo: " + file->Name;
 6             }
 7              else
 8             {
 9                 OutputTextBlock->Text = "Operation cancelled.";
10             }
11         }).then([ this](task< void> t)
12         {
13              try{
14                 t. get();
15             } catch(Platform::Exception^ e)
16             {
17                 OutputTextBlock->Text = e->Message
18             }
19         });
    
      额,希望你能明白,task为我们做了些什么东西。我来总结一下吧:
1. 没有了复杂的completed回调函数,操作完成之后,执行then中的代码,简单而明了。
2. then中的异步代码是在UI线程中的,没有Dispatcher->RunAsync方法,不是么?
3. 可以有多个then
4. 可以处理异常,不是更强大么?
      task为我们做的,就是从30多行复杂的代码精简到只有20行。

至于原理,大家可以阅读参考文献1,我暂时还没有怎么理解。。。

你可能感兴趣的:(温故而知新系列--Windows 8 的异步编程(二)可爱的task和then(await))