Introduction
This article provides a brief introduction to the use of Async and Await to make simple asynchronous calls from your C# 5.0/.NET 4.5 applications. Async and Await is a new language feature introduced with the .NET 4.5 update.
Async and Await simplify the code necessary to make asynchronous calls; within the mechanization, the compiler manages the complexity whilst the code remains relatively simple. As a developer, you may gain the benefits of this new functionality merely by marking methods using the new keyword "async".
Async and await are still open to the same sorts of problems one may encounter coding for asynchronous processing using other approaches, including deadlocks; the complexity of the code is still there, but it is generated for you and you don't need to write it. Whilst the mechanization simplifies the coding it does still require one to be vigilant towards the proper use of the new features.
Creating Asynchronous Methods
There are three approaches to creating asynchronous methods using the new features; each being defined by its return type.
- Methods that return void
- Methods that return Task
- Methods that return Task<T>
Call Syntax and Error Handling
The async keyword marks a method for the compiler. The await keyword signals the code to wait for the return of a long-running operation.
Async calls return almost immediately, leaving the main thread responsive whilst the longer-running process is kicked off to run in the background. The use of void returning methods is suitable only for occasions in which the actions completed by the thread are of no further concern to the main thread (for example, if the code is logging an event and the success of the logging task is of no importance to the main thread); void returning methods are suitable for the launch and forget about it sort of scenario. Methods returning Task are similar to void in as much as those methods do not return a value to the calling method either. If data is to be returned to the main thread and handled there, one should use a return type of Task<T>.
Error handling within methods using the three return types is different as well. When dealing with void returning methods, the error handling will need to be accomplished in the async method as it will not return to the calling thread. Similarly, Task return type methods should also handle their own errors. Error handling for methods returning Task<T> may be accomplished within the calling thread.
Rules for use
The following are the rules for use.
- The await keyword may not be inserted into a "lock" code block
- With try-catch-finally blocks, await may not be used in the catch or finally sections
- Async methods may not use "ref" or "out" parameters
- Class constructors may not be marked async nor may they use the await keyword
- Await may only be used in methods marked as async
- Property accessors may not be marked as async nor may they use await
Example 1. Void Returning Methods
In this example, the main thread will make a call to an async method with a void return type. In this example, an async method will be used to call a web service to fetch a weather report and display it to a console window. The web service used in this example can be found here.
All of the code necessary for this example is contained in the Program.cs file; there is a single void return type async method called by main. The async method is static as well. The code is fully commented so you can follow along with it by reading those comments.
The main method calls a void return type async method entitled, "GetGlobalWeather" that accepts a city and country name as its two required arguments. Just to prove the point that the main method is not waiting on the async method to complete, as soon as the call is made, it prints out a message in the console telling the user that the service has been called. If you look at the screenshot in Figure 1, you will see that this message was in fact printed to the screen whilst the service was fetching the weather report, as such, the weather report is actually printed out after the message.
When looking over the following code, notice that the error handling was placed into the async method, it would not be possible to catch and process any errors encountered from within the Main method. When using void returning methods, one should always place the error handling directly within the async method.
The code for this example is as follows.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SimpleAsyncAwait
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting call to service");
// make a call to a web service using an async method
GetGlobalWeather("Edinburgh", "United Kingdom");
// print out a line to show we are not waiting for the service call to complete
// but rather and pressing on with this main thread
Console.WriteLine("Call in progress...");
// wait for the user to finish reading the report
Console.ReadLine();
}
// since the return type is void, the method has to handle the data itself - so here
// we just dump the data to the screen, error handling has to be implemented
// here, as well as nothing, is returned to the calling thread
static async void GetGlobalWeather(string city, string country)
{
try
{
// create an instance of the client
GlobalWeather.GlobalWeatherSoapClient client = new GlobalWeather.GlobalWeatherSoapClient();
// set a string to the results of the weather query - use await to hold here for the results to
// be returned to the string variable
string weather = await client.GetWeatherAsync(city, country);
// once the string is populated, write it out to the console
Console.WriteLine(weather);
// report completion from the async method
Console.WriteLine("Service call complete");
}
catch (Exception ex)
{
// handle any errors here else risk an unhandled exception
Console.WriteLine("Error: " + ex.Message);
}
finally
{
// tell the user to click a key to close the console window
Console.WriteLine("Press any key to close this window");
}
}
}
}
![Console]()
Figure 1. Console Output for Example 1
Example 2. Task Returning Methods
In this example, the main thread will make a call to an async method with a Task return type. In this example, an async method will be used to call a web service to fetch a list of cities available for weather reports and display it to a console window. As in the previous example, the web service used in this example can be found here.
All of the code necessary for this example is contained in the Program.cs file; there is a single Task return type async method called by main. The async method is static as well. The code is fully commented so you can follow along by reading those comments.
The main method calls a Task return type async method entitled, "GetGlobalWeatherCities" that accepts a country name as its required argument. Once the async method is called, note that we can check the status of the operation (amongst other things). Note also that we can press on with the main thread whilst the async method executes; this is illustrated by printing out some nonsense characters to the console window after calling the method.
When looking over the following code, notice too that the error handling was placed into the async method, as with void returning async methods, it is best to handle any errors in the method itself although with the task return type we can monitor and control the thread from main.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsyncAwait_4
{
class Program
{
static void Main(string[] args)
{
// tell the user we are going to list available cities
Console.WriteLine("List of Available Cities");
// create a task and assign it to the async call
Task t = GetGlobalWeatherCities("France");
//, unlike void, since we have a return type of task, we can do things
// with it like check the status of the operation or wait for the task.
Console.WriteLine("Status: " + t.Status.ToString());
// To prove the main thread continues after the call, write some
// nonsense out the screen whilst the thread runs
WasteTime();
// hold the console open
Console.ReadLine();
}
/// <summary>
/// Async call to retrieve a collection of cities available for
/// weather reports
/// </summary>
/// <param name="country"></param>
/// <returns></returns>
static async Task GetGlobalWeatherCities(string country)
{
try
{
// create an instance of the client
GlobalWeather.GlobalWeatherSoapClient client =
new GlobalWeather.GlobalWeatherSoapClient();
// set up a variable to hold the result and use await to hold for the service
// to populate it before pressing on with the task
string cities = await client.GetCitiesByCountryAsync(country);
// once the string variable is populated, print it out to the console
Console.WriteLine(cities);
}
catch (Exception ex)
{
// best to handle the exception here if one is encountered
Console.WriteLine("Error: " + ex.Message);
}
}
/// <summary>
/// Waste time drawing out some trash to the
/// console whilst the async method runs out
/// </summary>
static void WasteTime()
{
for (int i = 0; i < 50; i++)
{
if (i % 2 == 0)
Console.Write("-");
else
Console.Write("|");
}
Console.WriteLine();
}
}
}
![Debug]()
Figure 2. Console Output for Example 2
Example 3. Task<T> Returning Methods
In this example, the main thread will make a call to an async method with a Task<T> return type. In this example, an async method will be used to call a web service to fetch a weather report and display it to a console window. As in the other examples, the web service used in this example can be found here.
All of the code necessary for this example is contained in the Program.cs file; there is a single Task<T> return type async method called by main. The async method is static as well. The code is fully commented so you can follow along with it by reading those comments.
The main method calls a Task<T> return type async method entitled, "GetGlobalWeather" that accepts a city name and a country name as its required arguments. Once the async method is called, note that we can check the status of the operation as we could with the Task return type. Note also that we can press on with the main thread whilst the async method executes; this is illustrated by printing a date string and a line separator to the console window after calling the method. The Task<T> return type async calls will block on the line that actually handles the returned value, in this case, a string containing a weather report.
When looking over the following code, notice that the error handling was placed into Main, a departure from how we handled errors on void and Task-returning async methods, you may still handle the errors in the async method if you'd like to do that, but you don't need to if you'd rather not.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsyncAwait_3
{
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Fetching Current Edinburgh Weather");
// wx is a placeholder that will be populated through the synchronization context once the method returns
Task<string> wx = GetGlobalWeather("Edinburgh", "United Kingdom");
// display the status of the task
Console.WriteLine("Task Status: " + wx.Status.ToString());
// even though we called the service, this will execute concurrently whilst the async call is in play
// and so the date will be displayed before the weather report
Console.WriteLine(DateTime.Now.ToLongTimeString());
Console.WriteLine("------------------------------------");
// handled when the value returns - this will block on the console.writeline until the result returns
Console.WriteLine(wx.Result);
}
catch (Exception ex)
{
// With Task<T>, we can handle the error in the calling thread
Console.WriteLine("Error: " + ex.Message);
}
finally
{
Console.WriteLine("Operation complete, press any key to terminate session");
Console.ReadLine();
}
}
/// <summary>
/// An async call returning Task of type string
/// </summary>
/// <param name="city"></param>
/// <param name="country"></param>
/// <returns></returns>
static async Task<string> GetGlobalWeather(string city, string country)
{
// note there is no error handling in the async method on this call, that is not to say
// one could not put error handling here but in this case it is possible to catch and handle
// errors in the calling thread
GlobalWeather.GlobalWeatherSoapClient client = new GlobalWeather.GlobalWeatherSoapClient();
// we will await the call to fetch the weather and return the result immediately to the caller
return await client.GetWeatherAsync(city, country);
}
}
}
![Project]()
Figure 3. Console Output for Example 3
![Output]()
Figure 4. An exception handled in the Main, outside the async method
Summary
In this article, we reviewed the rules for using Async and Await and provided examples of async methods returning void, Task, and Task<T>. The article further addressed some of the issues pertaining to error handling given each of the three potential return types. There is certainly much more to Async and Await than was covered in this simple introduction, and for that matter, with the Task Parallel Library (TPL) in the larger picture.
The article does try to illustrate how simple it is to implement this new language feature to make asynchronous calls within your C# 5.0 applications targeting the .NET 4.5 framework.