WhenAny:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic)

本主题中的示例将下载博客源的 Windows 运行时 类型以异步方式,与按完成顺序处理异步任务的 .NET Framework 方法结合。 有关此类型的更多信息,请参见 SyndicationClient。 有关方法的更多信息,请参见 Task.WhenAny

通过组合这些功能,可以同时下载多个博客源并在下载完成时处理结果。 如果一个源比其他源下载更快速,则其结果会首先出现。 使用 SyndicationClient 方法,您可以更轻松地下载源;使用 Task.WhenAny 方法,您可以更轻松地标识完成下载的下一个源。

备注

若要运行示例,您必须在计算机上安装 Windows 8。此外,如要从 Visual Studio 运行示例,还必须安装 Visual Studio 2012、Visual Studio 2013、Visual Studio express 2012 for Windows 8 或Visual Studio Express 2013 for Windows。

下面的代码结合了 Windows 运行时 和 .NET Framework 中的下列功能:

Try 
    Dim feedsQuery As IEnumerable(Of Task(Of SyndicationFeed)) =
        From uri In uriList
        Select client.RetrieveFeedAsync(uri).AsTask()
    ' AsTask changes the returns from RetrieveFeedAsync into tasks. 

    ' Run the query to start all the asynchronous processes. 
    Dim blogFeedTasksList As List(Of Task(Of SyndicationFeed)) = feedsQuery.ToList()

    Dim feed As SyndicationFeed

    ' Repeat the following until there are no tasks left: 
    '    - Grab the first one that finishes. 
    '    - Retrieve the results from the task (what the return statement  
    '      in RetrieveFeedAsync returns). 
    '    - Remove the task from the list. 
    '    - Display the results. 
    While blogFeedTasksList.Count > 0
        Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
        feed = Await nextTask
        blogFeedTasksList.Remove(nextTask)
        DisplayResults(feed)
    End While 

Catch ex As Exception
    ResultsTextBox.Text =
        "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
End Try
try
{
    IEnumerable<Task<SyndicationFeed>> feedsQuery =
            from uri in uriList
            // AsTask changes the returns from RetrieveFeedAsync into tasks. 
            select client.RetrieveFeedAsync(uri).AsTask();

    // Run the query to start all the asynchronous processes.
    List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();

    SyndicationFeed feed;

    // Repeat the following until no tasks remain: 
    //    - Grab the first one that finishes. 
    //    - Retrieve the results from the task (what the return statement  
    //      in RetrieveFeedAsync returns). 
    //    - Remove the task from the list. 
    //    - Display the results. 
    while (blogFeedTasksList.Count > 0)
    {
        Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
        feed = await nextTask;                    
        blogFeedTasksList.Remove(nextTask);
        DisplayResults(feed);
    }
}
catch (Exception ex)
{
    ResultsTextBox.Text =
        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
}

示例生成类似于下行的输出。 对于每个博客,屏幕显示博客的标题,后面是博客文章的标题和日期。

Developing for Windows
     New blog for Windows 8 app developers, 5/1/2012 2:33:02 PM -07:00
     Trigger-Start Services Recipe, 3/24/2011 2:23:01 PM -07:00
     . . .
     Countdown to PDC10, 10/26/2010 4:11:28 PM -07:00

Extreme Windows Blog
     PDXLAN 20: “Epidemic” Custom PC by Jon Hansz, 7/30/2012 2:31:35 PM -07:00
     Samsung Notebook Series 9: Taking Thin and Light to the Extreme, 7/23/2012 12:06:03 PM -07:00
     . . .
     AMD Unveils A-Series APUs, 6/13/2011 9:34:01 PM -07:00

Blogging Windows
     Windows 8 has reached the RTM milestone, 8/1/2012 9:00:00 AM -07:00
     Windows 8 will be available on…, 7/18/2012 1:09:00 PM -07:00
     . . .
     More buzz from BUILD – Developers get their devices!, 9/13/2011 7:47:57 PM -07:00

Springboard Series Blog
     What to Expect in User Experience Virtualization Beta 2, 6/25/2012 11:03:27 PM -07:00
     Introducing Microsoft BitLocker Administration 2.0 Beta, 6/12/2012 8:08:23 AM -07:00
     . . .
     The Springboard Series Visits Lima, Peru, 11/18/2011 5:27:37 AM -08:00

本主题其余部分提供有关如何创建示例及其工作原理的详细信息。

必须在计算机上安装 Visual Studio 2012 和 Windows 8 才能运行此应用程序。

本主题包括下列各节。

  • 示例的设置选项
  • 了解起始代码
  • 扩展起始代码
  • 下载起始代码
  • 下载完成的应用程序
  • 生成起始代码
  • 生成完成的应用程序
  • 相关主题

示例的设置选项

该示例依据快速入门:使用异步编程的时间运算符描述的博客读取器。 但是,此主题的起始代码下载多个博客源而不只是一个。

起始代码使用 Windows 运行时 功能来按顺序下载博客源。 即博客源按 URL 集合列表中的顺序下载。 已完成的应用程序从 .NET Framework 中添加功能,以按照博客源完成的顺序对其进行下载。

可以按以下任一方式设置示例代码:

  • 起始代码。

    • 可以按照下载起始代码中的说明下载起始代码。

    • 可以按照生成起始器代码中的说明自己创建起始代码。

    • 通过滚动到生成起始代码,可以查看起始代码而无需实现它。

  • 完成的应用程序。

    • 可以按照下载完成的应用程序中的说明下载完成的应用程序。

    • 按照生成完成的应用程序中的说明可以自己生成应用程序。

    • 通过滚动到生成完成的应用程序,可以查看完成的应用程序而无需实现它。

了解起始代码部分讨论基本解决方案中的关键点。

扩展起始代码一节演示如何通过添加 AsTask``2Task.WhenAny修改代码。

了解起始代码

启动程序代码使用 SyndicationClient 方法,即 RetrieveFeedAsync,可从 URI 列表中的每个 URI 下载博客源。 每次对该方法的调用都会返回一个表示正在进行的异步操作的 IAsyncOperationWithProgress 实例。 等待时,异步操作会生成包含下载博客源相关信息的 SyndicationFeed 实例。

代码定义一个应用 RetrieveFeedAsync 于 URI 列表每一项的查询。 执行后,该查询会返回 IAsyncOperationWithProgress 实例的集合。

Dim feedsQuery As IEnumerable(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                                RetrievalProgress)) =
                                                From uri In uriList
                                                Select client.RetrieveFeedAsync(uri)
IEnumerable<IAsyncOperationWithProgress<SyndicationFeed, 
    RetrievalProgress>> feedsQuery = from uri in uriList
                                     select client.RetrieveFeedAsync(uri);

ToList``1 运行查询并启动异步过程,如以下代码所示。

Dim blogFeedOpsList As List(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                           RetrievalProgress)) =
                                               feedsQuery.ToList()
List<IAsyncOperationWithProgress<SyndicationFeed, 
    RetrievalProgress>> blogFeedOpsList = feedsQuery.ToList();

此时,您会发现一个 IAsyncOperationWithProgress 活动实例列表。 仍必须等待每个实例获取最终结果。

以下循环等待每个 IAsyncOperationWithProgress 实例,以检索 SyndicationFeed 结果。

Dim feed As SyndicationFeed
For Each blogFeedOp In blogFeedOpsList
    ' The Await operator retrieves the final result (a SyndicationFeed instance) 
    ' from each IAsyncOperation instance.
    feed = Await blogFeedOp
    DisplayResults(feed)
Next
SyndicationFeed feed;
foreach (var blogFeedOp in blogFeedOpsList)
{
    // The await operator retrieves the final result (a SyndicationFeed instance) 
    // from each IAsyncOperation instance.
    feed = await blogFeedOp;
    DisplayResults(feed);
}

您可以在本主题末尾处的生成起始代码部分查看此版本的程序。

您可以在 Quickstart: using the await operator for asynchronous programming(快速入门:使用 await 运算符进行异步编程)中找到有关使用异步 Windows 运行时 API 进行编程的更多信息。

扩展起始代码

起始代码表明 SyndicationClient 便于下载博客源。 完成该示例的其余步骤将实现应用程序对博客源的处理,处理将按照博客源下载完成的顺序而不是其显示在 URI 列表中的顺序进行。

用于完成这种增强的键是 Task.WhenAny 方法。 当您将 WhenAny 应用到异步过程集合时,该方法会返回首个完成的过程,最大限度地减少所需等待的时间。 在本示例中,博客源信息的显示顺序并不重要。 如果一个博客下载很慢,则可能首先显示来自其他博客的结果。 WhenAny 的该情况似乎已是最理想的,除一点以外:WhenAny 需要一个任务集合。

调用 AsTask

WhenAny 需要 TaskTask 实例的集合,但是,下载博客源的 SyndicationClient 方法会返回 IAsyncOperationWithProgress 实例。 因此,该应用程序必须成为 Windows 运行时 中的 IAsyncOperationWithProgress 对象和 .NET Framework 中的 Task 对象之间的桥梁。

.NET Framework 提供 AsTask``2 扩展方法以进行转换。 当您在 IAsyncOperationWithProgress 实例中调用 AsTask 时,AsTask 会返回一个表示异步操作的任务。 当相应的 IAsyncOperationWithProgress 实例完成时任务也完成,并且任务具有实例的结果或异常。

因此,您仅调用了 RetrieveFeedAsync 返回的每个 IAsyncOperationWithProgress 实例上的 AsTask,如以下代码所示。 代码将变量重命名,以反映任务的更改,并为了清楚起见,采用显式类型。

Dim feedsQuery As IEnumerable(Of Task(Of SyndicationFeed)) =
    From uri In uriList
    Select client.RetrieveFeedAsync(uri).AsTask()
' AsTask changes the returns from RetrieveFeedAsync into tasks. 

' Run the query to start all the asynchronous processes. 
Dim blogFeedTasksList As List(Of Task(Of SyndicationFeed)) = feedsQuery.ToList()
IEnumerable<Task<SyndicationFeed>> feedsQuery =
        from uri in uriList
        // AsTask changes the returns from RetrieveFeedAsync into tasks. 
        select client.RetrieveFeedAsync(uri).AsTask();

// Run the query to start all the asynchronous processes.
List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();

应用 WhenAny

转换的最后一个步骤是将 Task.WhenAny 方法添加到应用程序中。 WhenAny 适用于任务集合 (blogFeedTasksList) 并可返回完成集合中的第一个任务。 具体而言,WhenAny 返回一个等待任务,该任务评估为首先完成的任务。

下面的语句调用 WhenAny 并等待其结果。 为了更清晰地显示结果,代码采用显式类型。

Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);

下面的代码与上一句执行相同的操作,但是将操作分为两个语句,以阐明所发生的情况。 第一个语句会调用 WhenAny,第二个语句则等待结果。

' WhenAny returns a task that, when awaited, produces a task.
' Call:
Dim whenAnyTask As Task(Of Task(Of SyndicationFeed)) = Task.WhenAny(blogFeedTasksList)
' Await:
Dim nextTask As Task(Of SyndicationFeed) = Await whenAnyTask
// WhenAny returns a task that, when awaited, produces a task.
// Call:
Task<Task<SyndicationFeed>> whenAnyTask = Task.WhenAny(blogFeedTasksList);
// Await:
Task<SyndicationFeed> nextTask = await whenAnyTask;

最后,您必须等待 nextTask 从首先完成的任务中检索结果 (SyndicationFeed 实例),然后必须从列表中移除 nextTask,这样您就不用再次处理。

feed = Await nextTask
blogFeedTasksList.Remove(nextTask)
feed = await nextTask;                    
blogFeedTasksList.Remove(nextTask);

使用 while 循环来执行 blogFeedTasksList 中的每个任务的这些步骤。

While blogFeedTasksList.Count > 0
    Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
    feed = Await nextTask
    blogFeedTasksList.Remove(nextTask)
    DisplayResults(feed)
End While
while (blogFeedTasksList.Count > 0)
{
    Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
    feed = await nextTask;                    
    blogFeedTasksList.Remove(nextTask);
    DisplayResults(feed);
}

您可以在本主题末尾处的生成完成的应用程序部分查看此版本的程序。 或者,可遵循下载完成的应用程序中的说明下载项目。

警告

在一个循环中使用 WhenAny,如该示例所述,对于涉及少量任务的问题是可行的。但是,如果您有大量任务需要处理,则其他方法更为有效。有关更多信息和示例,请参见Processing Tasks as they complete(完成时处理任务)。

下载起始代码

可以从 Async Sample: Bridging from .NET to Windows(Async 示例:从 .NET 桥接到 Windows)下载示例的起始代码。 如果您无权访问 Internet,请遵照本主题末尾的生成起始代码中的指令创建起始代码。

在下载代码后,您可以通过执行以下步骤来打开并运行。

  1. 解压下载的文件,然后启动 Visual Studio 2012。

  2. 在菜单栏上,依次选择**“文件”“打开”“项目/解决方案”**。

  3. 导航到保存解压缩示例代码的文件夹,然后打开 AsTaskWhenAnyDemoVB 或 AsTaskWhenAnyDemoCS 的解决方案 (.sln) 文件。

  4. 在“解决方案资源管理器”中,打开“SequentialBlogReader”项目的快捷菜单,然后选择“设置为启动项目”。

  5. 选择 F5 键生成并运行项目。

  6. 多次运行代码以验证结果每次出现的顺序一致。

您可以在本主题末尾处的生成起始代码部分查看 MainPage.xaml.vb 或 MainPage.xaml.cs 文件。

该示例依据快速入门:使用异步编程的时间运算符描述的博客读取器。 但是,此主题的起始代码下载多个博客源而不只是一个。

有关您可以对应用程序进行多种改进和扩展的信息,请参见 Create a blog reader(创建博客阅读器)。

下载完成的应用程序

如果您不希望自己生成示例,则可以下载完整示例。 遵照下载起始代码部分的说明,但选择“WhenAnyBlogReader”作为“启动项目”。

多次运行程序以验证博客源信息以不同的顺序显示。

您可以在本主题末尾处的生成完成的应用程序部分查看 MainPage.xaml.vb 或 MainPage.xaml.cs 文件。

生成起始代码

可以从 Async Sample: Bridging from .NET to Windows(Async 示例:从 .NET 桥接到 Windows)下载本主题的示例。 如果您希望自己安装应用程序,请执行以下步骤。

  1. 启动 Visual Studio 2012。

  2. 在菜单栏上,选择**“文件”“新建**、“项目”

    将打开**“新建项目”**对话框。

  3. 在“已安装应用程序”的“模板”类别中,选择“Visual Basic”或“Visual C#”,然后在项目类型列表中选择“Windows 应用商店”。

  4. 在项目类型列表中,选择“空白应用程序 (XAML)”。

  5. 将新项目命名为“SequentialBlogReader”,并选中“确定”按钮。

    新项目出现在**“解决方案资源管理器”**中。

  6. 在“解决方案资源管理器”中,打开 MainPage.xaml 的快捷菜单,然后选择“打开”。

  7. 在 MainPage.xaml 的“XAML”窗口中,使用下面的代码替换代码。

    <Page
        x:Class="SequentialBlogReader.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AsTaskWhenAnyDemo"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Stretch" Margin="325,128,330,0" VerticalAlignment="Top" Click="StartButton_Click" Height="71" Background="#FFA89B9B" FontWeight="Bold" FontSize="36"/>
            <TextBox x:Name="ResultsTextBox" Margin="325,222,330,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Page>
    

    包含文本框和一个按钮的简单窗口显示在 MainPage.xaml 的“设计”窗口中。

    有关您可以对 UI 进行多种改进和扩展的信息,请参见Create a blog reader(创建博客阅读器)。

  8. 在“解决方案资源管理器”中,打开“MainPage.xaml.vb”或“MainPage.xaml.cs”的快捷菜单,然后选择“查看代码”。

  9. 将 MainPage.xaml.vb 或 MainPage.xaml.cs 中的代码替换为以下代码。

    ' Add an Imports statement for SyndicationClient. 
    Imports Windows.Web.Syndication
    
    
    ' The Blank Page item template is documented at http:'go.microsoft.com/fwlink/?LinkId=234238 
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    
        End Sub 
    
    
        ' The async modifier enables you to use await in the event handler. 
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ResultsTextBox.Text = "" 
    
            ' Disable the button until the operation is complete.
            StartButton.IsEnabled = False 
    
            Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()
    
            ' Force the SyndicationClient to download the information.
            client.BypassCacheOnRetrieve = True 
    
            Dim uriList = CreateUriList()
    
            Try 
                Dim feedsQuery As IEnumerable(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                                                RetrievalProgress)) =
                                                                From uri In uriList
                                                                Select client.RetrieveFeedAsync(uri)
    
                ' Run the query to start all the asynchronous processes. 
                Dim blogFeedOpsList As List(Of IAsyncOperationWithProgress(Of SyndicationFeed, 
                                                                           RetrievalProgress)) =
                                                               feedsQuery.ToList()
    
                Dim feed As SyndicationFeed
                For Each blogFeedOp In blogFeedOpsList
                    ' The Await operator retrieves the final result (a SyndicationFeed instance) 
                    ' from each IAsyncOperation instance.
                    feed = Await blogFeedOp
                    DisplayResults(feed)
                Next 
    
            Catch ex As Exception
                ResultsTextBox.Text =
                    "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
            End Try 
    
            ' Reenable the button in case you want to run the operation again.
            StartButton.IsEnabled = True 
        End Sub 
    
    
        Function CreateUriList() As List(Of Uri)
    
            ' Create a list of URIs. 
            Dim uriList = New List(Of Uri) From
            {
                    New Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
            }
            Return uriList
        End Function 
    
    
        Sub DisplayResults(sf As SyndicationFeed)
    
            ' Title of the blog.
            ResultsTextBox.Text &= sf.Title.Text & vbCrLf
    
            ' Titles and dates for blog posts. 
            For Each item As SyndicationItem In sf.Items
    
                ResultsTextBox.Text &= vbTab & item.Title.Text & ", " &
                                    item.PublishedDate.ToString() & vbCrLf
            Next
    
            ResultsTextBox.Text &= vbCrLf
        End Sub 
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // Add a using directive for SyndicationClient. 
    using Windows.Web.Syndication;
    
    
    namespace SequentialBlogReader
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
    
                // Disable the button until the operation is complete.
                StartButton.IsEnabled = false;
    
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                var uriList = CreateUriList();
    
                try
                {
                    IEnumerable<IAsyncOperationWithProgress<SyndicationFeed, 
                        RetrievalProgress>> feedsQuery = from uri in uriList
                                                         select client.RetrieveFeedAsync(uri);
    
                    // Run the query to start all the asynchronous processes.
                    List<IAsyncOperationWithProgress<SyndicationFeed, 
                        RetrievalProgress>> blogFeedOpsList = feedsQuery.ToList();
    
                    SyndicationFeed feed;
                    foreach (var blogFeedOp in blogFeedOpsList)
                    {
                        // The await operator retrieves the final result (a SyndicationFeed instance) 
                        // from each IAsyncOperation instance.
                        feed = await blogFeedOp;
                        DisplayResults(feed);
                    }
                }
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
                }
    
                // Reenable the button in case you want to run the operation again.
                StartButton.IsEnabled = true;
            }
    
            List<Uri> CreateUriList()
            {
                // Create a list of URIs.
                List<Uri> uriList = new List<Uri> 
                { 
                    new Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for blog posts. 
                foreach (SyndicationItem item in sf.Items)
                {
                    ResultsTextBox.Text += "\t" + item.Title.Text + ", " +
                                        item.PublishedDate.ToString() + "\r\n";
                }
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. 选择 F5 键运行程序,然后选择“开始”按钮。

生成完成的应用程序

可以从 Async Sample: Bridging from .NET to Windows(Async 示例:从 .NET 桥接到 Windows)下载本主题的示例。 如果您希望自己安装应用程序,请执行以下步骤。

  1. 启动 Visual Studio 2012。

  2. 在菜单栏上,选择**“文件”“新建**、“项目”

    将打开**“新建项目”**对话框。

  3. 在“已安装应用程序”的“模板”类别中,选择“Visual Basic”或“Visual C#”,然后选择“Windows 应用商店”。

  4. 从项目模板列表中,选择“空白应用程序 (XAML)”。

  5. 将项目命名为“WhenAnyBlogReader”,并选中“确定”按钮。

    新项目出现在**“解决方案资源管理器”**中。

  6. 在“解决方案资源管理器”中,打开 MainPage.xaml 的快捷菜单,然后选择“打开”。

  7. 在 MainPage.xaml 的“XAML”窗口中,使用下面的代码替换代码。

    <Page
        x:Class="WhenAnyBlogReader.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AsTaskWhenAnyDemo"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Stretch" Margin="325,128,330,0" VerticalAlignment="Top" Click="StartButton_Click" Height="71" Background="#FFA89B9B" FontWeight="Bold" FontSize="36"/>
            <TextBox x:Name="ResultsTextBox" Margin="325,222,330,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Page>
    

    包含文本框和一个按钮的简单窗口显示在 MainPage.xaml 的“设计”窗口中。

    有关您可以对应用程序进行多种改进和扩展的信息,请参见 Create a blog reader(创建博客阅读器)。

  8. 在“解决方案资源管理器”中,打开“MainPage.xaml.vb”或“MainPage.xaml.cs”的快捷菜单,然后选择“查看代码”。

  9. 将 MainPage.xaml.vb 或 MainPage.xaml.cs 中的代码替换为以下代码。

    ' Add an Imports statement for SyndicationClient. 
    Imports Windows.Web.Syndication
    
    ' Add an Imports statement for the Tasks. 
    Imports System.Threading.Tasks
    
    ' The Blank Page item template is documented at http:'go.microsoft.com/fwlink/?LinkId=234238 
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        End Sub 
    
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    
            ResultsTextBox.Text = "" 
    
            ' Disable the button until the operation is complete.
            StartButton.IsEnabled = False 
    
            Dim client As Windows.Web.Syndication.SyndicationClient = New SyndicationClient()
    
            ' Force the SyndicationClient to download the information.
            client.BypassCacheOnRetrieve = True 
    
            Dim uriList = CreateUriList()
    
            ' The following code avoids the use of implicit typing so that you  
            ' can see the types clearly. 
    
            Try 
                Dim feedsQuery As IEnumerable(Of Task(Of SyndicationFeed)) =
                    From uri In uriList
                    Select client.RetrieveFeedAsync(uri).AsTask()
                ' AsTask changes the returns from RetrieveFeedAsync into tasks. 
    
                ' Run the query to start all the asynchronous processes. 
                Dim blogFeedTasksList As List(Of Task(Of SyndicationFeed)) = feedsQuery.ToList()
    
                Dim feed As SyndicationFeed
    
                ' Repeat the following until there are no tasks left: 
                '    - Grab the first one that finishes. 
                '    - Retrieve the results from the task (what the return statement  
                '      in RetrieveFeedAsync returns). 
                '    - Remove the task from the list. 
                '    - Display the results. 
                While blogFeedTasksList.Count > 0
                    Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
                    feed = Await nextTask
                    blogFeedTasksList.Remove(nextTask)
                    DisplayResults(feed)
                End While 
    
            Catch ex As Exception
                ResultsTextBox.Text =
                    "Page could not be loaded." & vbCrLf & "Exception: " & ex.ToString()
            End Try 
    
            ' Reenable the button in case you want to run the operation again.
            StartButton.IsEnabled = True 
        End Sub 
    
    
        Function CreateUriList() As List(Of Uri)
    
            ' Create a list of URIs. 
            Dim uriList = New List(Of Uri) From
            {
                    New Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    New Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
            }
            Return uriList
        End Function 
    
    
        Sub DisplayResults(sf As SyndicationFeed)
    
            ' Title of the blog.
            ResultsTextBox.Text &= sf.Title.Text & vbCrLf
    
            ' Titles and dates for blog posts. 
            For Each item As SyndicationItem In sf.Items
    
                ResultsTextBox.Text &= vbTab & item.Title.Text & ", " &
                                    item.PublishedDate.ToString() & vbCrLf
            Next
    
            ResultsTextBox.Text &= vbCrLf
        End Sub 
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // Add a using directive for SyndicationClient. 
    using Windows.Web.Syndication;
    
    // Add a using directive for the Tasks. 
    using System.Threading.Tasks;
    
    
    namespace WhenAnyBlogReader
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                ResultsTextBox.Text = "";
    
                // Disable the button until the operation is complete.
                StartButton.IsEnabled = false;
    
                Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();
    
                // Force the SyndicationClient to download the information.
                client.BypassCacheOnRetrieve = true;
    
                var uriList = CreateUriList();
    
                // The following code avoids the use of implicit typing (var) so that you  
                // can identify the types clearly. 
    
                try
                {
                    IEnumerable<Task<SyndicationFeed>> feedsQuery =
                            from uri in uriList
                            // AsTask changes the returns from RetrieveFeedAsync into tasks. 
                            select client.RetrieveFeedAsync(uri).AsTask();
    
                    // Run the query to start all the asynchronous processes.
                    List<Task<SyndicationFeed>> blogFeedTasksList = feedsQuery.ToList();
    
                    SyndicationFeed feed;
    
                    // Repeat the following until no tasks remain: 
                    //    - Grab the first one that finishes. 
                    //    - Retrieve the results from the task (what the return statement  
                    //      in RetrieveFeedAsync returns). 
                    //    - Remove the task from the list. 
                    //    - Display the results. 
                    while (blogFeedTasksList.Count > 0)
                    {
                        Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
                        feed = await nextTask;                    
                        blogFeedTasksList.Remove(nextTask);
                        DisplayResults(feed);
                    }
                }
                catch (Exception ex)
                {
                    ResultsTextBox.Text =
                        "Page could not be loaded.\n\r" + "Exception: " + ex.ToString();
                }
    
                // Reenable the button in case you want to run the operation again.
                StartButton.IsEnabled = true;
            }
    
    
            List<Uri> CreateUriList()
            {
                // Create a list of URIs.
                List<Uri> uriList = new List<Uri> 
                { 
                    new Uri("https://windowsteamblog.com/windows/b/developers/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/extremewindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/bloggingwindows/atom.aspx"),
                    new Uri("https://windowsteamblog.com/windows/b/springboard/atom.aspx")
                };
                return uriList;
            }
    
    
            void DisplayResults(SyndicationFeed sf)
            {
                // Title of the blog.
                ResultsTextBox.Text += sf.Title.Text + "\r\n";
    
                // Titles and dates for blog posts. 
                foreach (SyndicationItem item in sf.Items)
                {
                    ResultsTextBox.Text += "\t" + item.Title.Text + ", " +
                                        item.PublishedDate.ToString() + "\r\n";
                }
                ResultsTextBox.Text += "\r\n";
            }
        }
    }
    
  10. 选择 F5 键运行程序,然后选择“开始”按钮。

请参见

参考

WhenAny``1

AsTask``1

概念

使用 Async 和 Await 的异步编程(C# 和 Visual Basic)

在完成一个异步任务后取消剩余任务(C# 和 Visual Basic)

启动多个异步任务并在其完成时进行处理(C# 和 Visual Basic)

其他资源

Quickstart: using the await operator for asynchronous programming

(快速入门:使用异步编程的时间运算符)创建一个博客阅读器

IAsyncOperationWithProgress