Today we announced the Visual Studio Async CTP, which shows one of the major features we plan to include in a future release of C# and Visual Basic. This feature makes development of asynchronous applications--which include everything from desktop applications with responsive UI to sophisticated web applications--much easier.
The future release will introduce two new keywords to the C# language: await and async. The await keyword is used to mark asynchronous calls, so that you don’t need to create callback functions anymore and can write code in the same way as if it were synchronous. The compiler will do all of the heavy lifting for you. The async keyword must be presented in the signature of the method that makes asynchronous calls. Briefly, you can use the await keyword only if the async keyword is in the method signature. The same is true for lambda expressions.
This post presents just a short description of the feature. I strongly recommend that you visit the Visual Studio Async CTP page to learn more of the details and get tons of useful resources, including the language spec and cool samples.
Although we have plenty of examples and resources available, it’s never enough. So I’m giving you one more demonstration of how much easier it might be to create asynchronous programs with a future version of the C# and Visual Basic languages.
First, some preparatory steps to make the example work:
1. Download the Visual Studio Async CTP and install it.
2. Open Visual Studio 2010 and create a new Silverlight 4 project (To make things easier later, name it FacebookAsync.)
3. Add a reference to AsyncCtpLibrary_Silverlight.dll. It should be in My Documents/Microsoft Visual Studio Async CTP/Samples.
4. Add a reference to the System.Json library, because it’s used in the example. This is a standard Silverlight library.
5. Go to the properties of the web project and, on the Web page, check what port it uses for the Visual Studio Development Server. My sample application uses the port 50750, so it might be easier for you to use the same one.
6. Register your application on Facebook. You will get an application ID (which is the same as a client ID) and a secret key.
The FacebookAsync application I want to show you downloads a list of friends from Facebook and displays their names and Facebook IDs. It has a button and a DataGrid control that autogenerates columns. Here is its XAML.
<UserControl x:Class="FacebookAsync.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="445" d:DesignWidth="514" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid AutoGenerateColumns="True" Height="282" HorizontalAlignment="Left" Margin="20,69,0,0" Name="searchResultsGrid" VerticalAlignment="Top" Width="390" />
<Button Content="Get list of friends from Facebook" Height="23" HorizontalAlignment="Left" Margin="25,21,0,0" Name="button1" VerticalAlignment="Top" Width="206" Click="button1_Click" />
Grid>
UserControl>
To get a list of friends, an application needs to perform several steps (according to Facebook Graph API), including making two asynchronous calls:
1. You need to redirect from the application page to the Facebook authorization page and provide a client ID, a secret key, and a return URL as parameters of the URL. The user should authorize the application. (In debugging mode the user is probably you, so you should have your own Facebook account.)
2. If the user authorizes the application, the Facebook API redirects to a return URL, which your application provides. Facebook also adds an authorization code into its redirect URL as a parameter. No asynchronous calls are made yet, because this is done by simply navigating between pages.
3. The application should exchange the authorization code to an access token by using the GET request. Remember that Silverlight doesn’t have a synchronous API, so you have to make an asynchronous call.
4. After the application gets a token, it makes a new GET request to the Facebook API to get a list of friends for the current user. Once again, this call should be asynchronous.
5. The application parses the response from Facebook and displays it on the page.
The full code of the version that doesn’t use the new async feature might look like this.
public partial class MainPage : UserControl
{
// Remember to change to your application URL.
string redirect_uri = @"http://localhost:50750/FacebookAsyncTestPage.aspx";
// Get these values by authorizing your application on Facebook.
string client_id = "Your Application ID";
string client_secret = "Your Secret Key";
// This collection will contain the list of friends.
ObservableCollection<FacebookFriend> searchResults =
new ObservableCollection<FacebookFriend>();
public MainPage()
{
InitializeComponent();
searchResultsGrid.ItemsSource = searchResults;
// Check whether it was a redirect from the Facebook authorization page.
if (HtmlPage.Document.DocumentUri.Query.Contains("?code="))
{
GetFriends();
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
// Navigating to a Facebook authorization page.
// No asynchronous calls are necessary.
var facebookAuthUri =
new StringBuilder(@"https://graph.facebook.com/oauth/authorize?");
facebookAuthUri.Append("client_id=").Append(client_id)
.Append(@"&redirect_uri=").Append(redirect_uri);
HtmlPage.Window.Navigate(new Uri(facebookAuthUri.ToString()));
}
public void GetFriends()
{
var code = HtmlPage.Document.DocumentUri.Query.TrimStart('?');
var facebookUri =
new StringBuilder(@"https://graph.facebook.com/oauth/access_token?");
facebookUri.Append("client_id=").Append(client_id)
.Append(@"&redirect_uri=").Append(redirect_uri)
.Append(@"&client_secret=").Append(client_secret)
.Append('&').Append(code);
// First asynchronous call, to get an access token.
WebClient proxy = new WebClient();
proxy.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(OnDownloadCompleted_Token);
proxy.DownloadStringAsync(new Uri(facebookUri.ToString()));
}
// Processing results of the first asynchronous call.
void OnDownloadCompleted_Token(
object sender, DownloadStringCompletedEventArgs e)
{
// Second asynchronous call, to get a list of friends.
WebClient proxy = new WebClient();
proxy.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(OnDownloadCompleted_Friends);
var facebookFriendsUri = new StringBuilder(
@"https://graph.facebook.com/me/friends?fields=id,name&");
facebookFriendsUri.Append((String)e.Result);
proxy.DownloadStringAsync(new Uri(facebookFriendsUri.ToString()));
}
// Processing results of the second asynchronous call.
void OnDownloadCompleted_Friends(
object sender, DownloadStringCompletedEventArgs e)
{
JsonObject jsonObject = (JsonObject)JsonValue.Parse((String)e.Result);
JsonArray jsonArray = (JsonArray)jsonObject["data"];
foreach (var friend in jsonArray)
{
searchResults.Add(
new FacebookFriend() {
Name = friend["name"], ID = friend["id"] });
}
}
}
// Items displayed in the data grid.
public class FacebookFriend
{
public string Name { get; set; }
public string ID { get; set; }
}
This is what you typically see when you have to deal with asynchronous applications: lots of callbacks, and overall the code is rather hard to read, although I spent some time on beautifying it. Now, let’s see how much easier it might be with the new async feature.
XAML will not change. The same is true for the FacebookFriend class, the button’s event handler, and all the properties in the MainPage class.
The changes start, unsurprisingly, in the GetFriends method. I’m going to make asynchronous calls by using the await keyword. But to enable this, I have to add the async keyword to the method signature.
public async void GetFriends()
Now I can start changing the code within the method. Let’s take a look at the existing code:
// First asynchronous call, to get an access token.
WebClient proxy = new WebClient();
proxy.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(OnDownloadCompleted_Token);
proxy.DownloadStringAsync(new Uri(facebookUri.ToString()));
Instead of making a callback, you can get the result string by using the await keyword.
// First asynchronous call, to get an access token.
WebClient proxy = new WebClient();
String token = await proxy.DownloadStringTaskAsync(
new Uri(facebookUri.ToString()));
Note that I used a different method here: DownloadStringTaskAsync. This method doesn’t exist in Silverlight today; it’s a part of the CTP release only. In addition to new keywords in the C# and Visual Basic languages, future versions of the .NET Framework and Silverlight need to implement the Task-based Asynchronous Pattern (TAP). The details about this pattern are also included into the CTP release; look for “The Task-based Asynchronous Pattern” document.
According to this pattern, asynchronous methods should follow a certain naming convention: They should have the “Async” postfix (or “TaskAsync” if a method with the “Async” postfix already exists in the given API, as with the WebClient class). Such methods return instances of the Task or Task class from the Task Parallel Library. But once again, the compiler does some work for you when you use the await keyword. In my example, the type of the token variable is not Task, but just String; no extra work such as conversion or type casting is necessary.
And now you can simply continue writing your application logic in the same method! Everything I had in the OnDownloadCompleted_Token method can be moved to the GetFriends method. I don’t even need to create a new WebClient, since I already have one.
So, the code
// Second asynchronous call, to get a list of friends.
WebClient proxy = new WebClient();
proxy.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(OnDownloadCompleted_Friends);
var facebookFriendsUri = new StringBuilder(
@"https://graph.facebook.com/me/friends?fields=id,name&");
facebookFriendsUri.Append((String)e.Result);
proxy.DownloadStringAsync(new Uri(facebookFriendsUri.ToString()));
from the OnDownloadCompleted_Token() method can be replaced with the following code (once again, using the new await keyword) and moved to the GetFriends method.
// Second asynchronous call, to get a list of friends.
var facebookFriendsUri = new StringBuilder(
@"https://graph.facebook.com/me/friends?fields=id,name&");
facebookFriendsUri.Append(token);
String friends = await proxy.DownloadStringTaskAsync(
new Uri(facebookFriendsUri.ToString()));
After that I can simply delete the OnDownloadCompleted_Token method. In fact, I can make everything even shorter. I don’t need to create the token variable, because I can use the await keyword right in the method call, like this:
var facebookFriendsUri = new StringBuilder(
@"https://graph.facebook.com/me/friends?fields=id,name&");
WebClient proxy = new WebClient();
// First asynchronous call, to get an access token.
facebookFriendsUri.Append(await proxy.DownloadStringTaskAsync(
new Uri(facebookUri.ToString())));
// Second asynchronous call, to get a list of friends.
String friends = await proxy.DownloadStringTaskAsync(
new Uri(facebookFriendsUri.ToString()));
The last step is to simply move the code from the OnDownloadCompleted_Friends method to the GetFriends method and delete the OnDownloadCompleted_Friends method.
Here’s what the final version of the GetFriends method looks like:
public async void GetFriends()
{
var code = HtmlPage.Document.DocumentUri.Query.TrimStart('?');
var facebookUri = new StringBuilder(
@"https://graph.facebook.com/oauth/access_token?");
facebookUri.Append("client_id=").Append(client_id)
.Append(@"&redirect_uri=").Append(redirect_uri)
.Append(@"&client_secret=").Append(client_secret)
.Append('&').Append(code);
var facebookFriendsUri = new StringBuilder(
@"https://graph.facebook.com/me/friends?fields=id,name&");
WebClient proxy = new WebClient();
// First asynchronous call, to get an access token.
facebookFriendsUri.Append(await proxy.DownloadStringTaskAsync(
new Uri(facebookUri.ToString())));
// Second asynchronous call, to get a list of friends.
String friends = await proxy.DownloadStringTaskAsync(
new Uri(facebookFriendsUri.ToString()));
JsonObject jsonObject = (JsonObject)JsonValue.Parse(friends);
JsonArray jsonArray = (JsonArray)jsonObject["data"];
foreach (var friend in jsonArray)
{
searchResults.Add(new FacebookFriend() {
Name = friend["name"], ID = friend["id"] });
}
}
An article from MS