WhenAny: Bridging between the .NET Framework and the Windows Runtime (C# and Visual Basic)
The example in this topic combines a Windows Runtime type that downloads blog feeds asynchronously with a .NET Framework method that processes asynchronous tasks in the order in which they complete. For more information about the type, see SyndicationClient. For more information about the method, see Task.WhenAny.
By combining these features, you can start to download multiple blog feeds simultaneously and process the results as they finish. If one feed downloads more quickly than the others, its results appear first. By using a SyndicationClient method, you can download the feeds more easily; by using the Task.WhenAny method, you can more easily identify the next feed that's finished downloading.
Note
To run the example, you must have Windows 8 installed on your computer. In addition, if you want to run the example from Visual Studio, you must also have Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 for Windows 8, or Visual Studio Express 2013 for Windows installed.
The following code combines these features from the Windows Runtime and the .NET Framework:
SyndicationClient.RetrieveFeedAsync downloads the blog feeds and returns IAsyncOperationWithProgress instances.
The AsTask extension method represents the IAsyncOperationWithProgress instances as Task<TResult> instances.
WhenAny returns a task that identifies the first task in a collection of tasks to complete.
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();
}
The example produces output that resembles the following lines. For each blog, the display shows the title of the blog followed by the titles and dates for blog posts.
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
The remainder of this topic provides details about how to create the example and how it works.
You must have Visual Studio 2012 and Windows 8 installed on your computer to run this app.
This topic contains the following sections.
- Setup Options for the Example
- Understanding the Starter Code
- Extending the Starter Code
- Downloading the Starter Code
- Downloading the Finished App
- Building the Starter Code
- Building the Finished App
- Related Topics
Setup Options for the Example
The example is based on the blog reader that's described in Quickstart: using the await operator for asynchronous programming. However, the starter code for this topic downloads multiple blog feeds instead of just one.
The starter code uses Windows Runtime functionality to download the blog feeds sequentially. That is, the blog feeds are downloaded in the order in which they are listed in a collection of URLs. The finished app adds functionality from the .NET Framework to download the blog feeds in the order in which they complete.
You can set up the example code in any of the following ways:
Starter code.
You can download the starter code by following the instructions in Downloading the Starter Code,
You can create the starter code yourself by following the instructions in Building the Starter Code.
You can review the starter code without implementing it by by scrolling to Building the Starter Code.
Finished app.
You can download the finished app by following the instructions in Downloading the Finished App
You can build the app yourself by following the instructions in Building the Finished App.
You can review the finished app without implementing it by scrolling to Building the Finished App.
The Understanding the Starter Code section discusses key points in the basic solution.
The Extending the Starter Code section shows you how to modify the code by adding AsTask and Task.WhenAny.
Understanding the Starter Code
The starter code uses a SyndicationClient method, RetrieveFeedAsync, to download a blog feed from each URI in a list of URIs. Each call to the method returns an IAsyncOperationWithProgress instance that represents an ongoing asynchronous operation. When awaited, the asynchronous operation produces a SyndicationFeed instance that contains information about the downloaded blog feed.
The code defines a query that applies RetrieveFeedAsync to each entry in a list of URIs. When executed, the query returns a collection of IAsyncOperationWithProgress instances.
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<TSource> runs the query and starts the asynchronous processes, as the following code shows.
Dim blogFeedOpsList As List(Of IAsyncOperationWithProgress(Of SyndicationFeed,
RetrievalProgress)) =
feedsQuery.ToList()
List<IAsyncOperationWithProgress<SyndicationFeed,
RetrievalProgress>> blogFeedOpsList = feedsQuery.ToList();
At this point, you have a list of active IAsyncOperationWithProgress instances. You still must await each instance to get the final results.
The following loop awaits each IAsyncOperationWithProgress instance to retrieve the SyndicationFeed results.
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);
}
You can review this version of the program in the Building the Starter Code section at the end of the topic.
You can find more information about programming with asynchronous Windows Runtime APIs in Quickstart: using the await operator for asynchronous programming.
Extending the Starter Code
The starter code demonstrates that SyndicationClient makes it easy to download blog feeds. The remaining step to complete the example is to enable the application to process the blog feeds in the order in which their downloads complete instead of the order in which they appear in the list of URIs.
The key to accomplishing the enhancement is the Task.WhenAny method. When you apply WhenAny to a collection of asynchronous processes, the method returns the first process that completes, minimizing the time that you must wait. In this example, the order in which the blog feed information appears isn't important. If one download is slow, the results from another blog can display first. The situation seems perfect for WhenAny except for one thing: WhenAny requires a collection of tasks.
Invoking AsTask
WhenAny requires a collection of Task or Task<TResult> instances, but the SyndicationClient method that downloads the blog feeds returns an IAsyncOperationWithProgress instance. Therefore, the app must bridge between the IAsyncOperationWithProgress objects from the Windows Runtime and Task objects from the .NET Framework.
The .NET Framework provides AsTask extension methods to make the transition. When you invoke AsTask on an IAsyncOperationWithProgress instance, AsTask returns a task that represents the asynchronous operation. The task completes when the corresponding IAsyncOperationWithProgress instance completes, and the task has the instance's result or exception.
Therefore, you just invoke AsTask on each IAsyncOperationWithProgress instance that RetrieveFeedAsync returns, as the following code shows. The code renames the variables to reflect the change to tasks and uses explicit typing for clarity.
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();
Note
AsTask plays an important role in async programming that you probably aren't aware of. The compiler uses AsTask whenever you apply an await operator to an IAsyncAction or IAsyncOperation instance, as the following code shows.
' What you write. Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(feedUri) ' What the compiler does. ' Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(feedUri).AsTask()
// What you write. SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri); // What the compiler does. //SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri).AsTask();
Applying WhenAny
The last step in the conversion is to add the Task.WhenAny method to the app. WhenAny is applied to a collection of tasks (blogFeedTasksList) and returns the first task in the collection that completes. More specifically, WhenAny returns a task that, when awaited, evaluates to the task that finished first.
The following statement calls WhenAny and awaits the result. The code uses explicit typing to show the result more clearly.
Dim nextTask As Task(Of SyndicationFeed) = Await Task.WhenAny(blogFeedTasksList)
Task<SyndicationFeed> nextTask = await Task.WhenAny(blogFeedTasksList);
The following code does the same thing as the previous statement but breaks the operation into two statements to clarify what happens. The first statement calls WhenAny, and the second statement awaits the result.
' 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;
Finally, you must await nextTask to retrieve the results (a SyndicationFeed instance) from the task that finished first, and then you must remove nextTask from the list so that you don't process it again.
feed = Await nextTask
blogFeedTasksList.Remove(nextTask)
feed = await nextTask;
blogFeedTasksList.Remove(nextTask);
Use a while loop to perform these steps for each task in 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);
}
You can review this version of the program in the Building the Finished App section at the end of the topic. Or you can follow the instructions in Downloading the Finished App to download the project.
Warning
The use of WhenAny in a loop, as described in the example, is fine for problems that involve a small number of tasks. However, other approaches are more efficient if you have a large number of tasks to process. For more information and examples, see Processing Tasks as they complete.
Downloading the Starter Code
You can download the starter code for the example from Async Sample: Bridging from .NET to Windows. If you don't have access to the Internet, follow the instructions in Building the Starter Code at the end of this topic to create the starter code.
After you download the code, you open and run it by performing the following steps.
Decompress the file that you downloaded, and then start Visual Studio 2012.
On the menu bar, choose File, Open, Project/Solution.
Navigate to the folder that holds the decompressed sample code, and open the solution (.sln) file for AsTaskWhenAnyDemoVB or AsTaskWhenAnyDemoCS.
In Solution Explorer, open the shortcut menu for the SequentialBlogReader project, and then choose Set as StartUp Project.
Choose the F5 key to build and run the project.
Run the code several times to verify that the results appear in the same order each time.
You can review the MainPage.xaml.vb or MainPage.xaml.cs file in the Building the Starter Code section at the end of the topic.
The example is based on the blog reader that's described in Quickstart: using the await operator for asynchronous programming. However, the starter code for this topic downloads multiple blog feeds instead of just one.
For information about a wide variety of improvements and extensions that you can make to the application, see Create a blog reader.
Downloading the Finished App
If you don't want to build the example yourself, you can download the complete example. Follow the instructions in the Downloading the Starter Code section, but choose WhenAnyBlogReader as the StartUp Project.
Run the program several times to verify that the blog feeds appear in different orders.
You can review the MainPage.xaml.vb or MainPage.xaml.cs file in the Building the Finished App section at the end of the topic.
Building the Starter Code
You can download the examples in this topic from Async Sample: Bridging from .NET to Windows. If you prefer to set the application up yourself, follow these steps.
Start Visual Studio 2012.
On the menu bar, choose File, New, Project.
The New Project dialog box opens.
In the Installed, Templates category, choose Visual Basic or Visual C#, and then choose Windows Store in the list of project types.
In the list of project types, choose Blank App (XAML).
Name the project SequentialBlogReader, and then choose the OK button.
The new project appears in Solution Explorer.
In Solution Explorer, open the shortcut menu for MainPage.xaml, and then choose Open.
In the XAML window of MainPage.xaml, replace the code with the following code.
<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>
A simple window that contains a text box and a button appears in the Design window of MainPage.xaml.
For information about a wide variety of improvements and extensions that you can make to the UI, see Create a blog reader.
In Solution Explorer, open the shortcut menu for MainPage.xaml.vb or MainPage.xaml.cs, and then choose View Code.
Replace the code in MainPage.xaml.vb or MainPage.xaml.cs with the following code.
' 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"; } } }
Choose the F5 key to run the program, and then choose the Start button.
Building the Finished App
You can download the examples in this topic from Async Sample: Bridging from .NET to Windows. If you prefer to set the application up yourself, follow these steps.
Start Visual Studio 2012.
On the menu bar, choose File, New, Project.
The New Project dialog box opens.
In the Installed, Templates category, choose Visual Basic or Visual C#, and then choose Windows Store.
From the list of project types, choose Blank App (XAML).
Name the project WhenAnyBlogReader, and then choose the OK button.
The new project appears in Solution Explorer.
In Solution Explorer, open the shortcut menu for MainPage.xaml, and then choose Open.
In the XAML window of MainPage.xaml, replace the code with the following code.
<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>
A simple window that contains a text box and a button appears in the Design window of MainPage.xaml.
For information about a wide variety of improvements and extensions that you can make to the application, see Create a blog reader.
In Solution Explorer, open the shortcut menu for MainPage.xaml.vb or MainPage.xaml.cs, and then choose View Code.
Replace the code in MainPage.xaml.vb or MainPage.xaml.cs with the following code.
' 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"; } } }
Choose the F5 key to run the program, and then choose the Start button.
See Also
Reference
Concepts
Asynchronous Programming with Async and Await (C# and Visual Basic)
Cancel Remaining Async Tasks after One Is Complete (C# and Visual Basic)
Start Multiple Async Tasks and Process Them As They Complete (C# and Visual Basic)
Other Resources
Quickstart: using the await operator for asynchronous programming