async 11: Control Flow and await

The next set of Async samples shows how the await keyword integrates into the familiar control flow constructs in the C# language. There are seven different samples in this section, and I won’t show all of them. Again, I’ll encourage you to visit Lucian’s site and try them yourself.

The first sample in this set clearly illustrates the advantages of the new syntax. The version using await enables you to see the branching logic easily:

        public async void AsyncIfElse()
{
if (DateTime.Now.DayOfWeek == DayOfWeek.Saturday ||
DateTime.Now.DayOfWeek == DayOfWeek.Sunday)
{
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/om/marine/home.htm")));
Console.WriteLine("It's the weekend! Time for the marine forecast!");
}
else
{
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov")));
Console.WriteLine("Back to work!");
}

WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/forecasts/graphical/")));
Console.WriteLine("Always useful to get a general forecast!");
}

Notice that await appears in the if branch, the else branch, and toward the end of the method. This will await the result of each of those actions, as you’ve seen in previous blog entries.

Contrast the clarity of the method above with the equivalent code using C# 4.0 syntax:

        public
        void AsyncIfElseBefore()
{
WebClient client = new WebClient();

if (DateTime.Now.DayOfWeek == DayOfWeek.Saturday ||
DateTime.Now.DayOfWeek == DayOfWeek.Sunday)
{
client.DownloadStringCompleted +=
AsyncIfElseBefore_Weekend_DownloadStringCompleted;
client.DownloadStringAsync(
new Uri("http://www.weather.gov/om/marine/home.htm"));
}
else
{
client.DownloadStringCompleted +=
AsyncIfElseBefore_Weekday_DownloadStringCompleted;
client.DownloadStringAsync(new Uri("http://www.weather.gov"));
}
}

void AsyncIfElseBefore_Weekend_DownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
{
WriteLinePageTitle(e.Result);

Console.WriteLine("It's the weekend! Time for the marine forecast!");

AsyncIfElseBefore_GeneralForecast();
}

void AsyncIfElseBefore_Weekday_DownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
{
WriteLinePageTitle(e.Result);

Console.WriteLine("Back to work!");

AsyncIfElseBefore_GeneralForecast();
}

void AsyncIfElseBefore_GeneralForecast()
{
WebClient client = new WebClient();

client.DownloadStringCompleted +=
AsyncIfElseBefore_GeneralForecast_DownloadStringCompleted;
client.DownloadStringAsync(
new Uri("http://www.weather.gov/forecasts/graphical/"));
}

void AsyncIfElseBefore_GeneralForecast_DownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
{
WriteLinePageTitle(e.Result);

Console.WriteLine("Always useful to get a general forecast!");
}

Ok, this version is obviously much longer. More than that, the original control flow gets lots among all the event handlers. One simple method suddenly grew to five  methods that connect and weave continuations to the original tasks. This sample, more than any other in this set shows how the new syntax makes it easier to write asynchronous code and still preserve the core logic of the original algorithm.

I’ll show two other samples.  The first shows a method with a switch statement. And inside each case of the switch statement, the method awaits. That means the entire method is async:

        public async void AsyncSwitch()
{
double stockPrice = 123.45;

switch (DateTime.Now.DayOfWeek)
{
case DayOfWeek.Monday:
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/alerts-beta/wa.php?x=1")));
stockPrice += 1.25;
break;
case DayOfWeek.Tuesday:
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/alerts-beta/or.php?x=1")));
stockPrice *= 1.04;
break;
case DayOfWeek.Wednesday:
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/alerts-beta/ca.php?x=1")));
stockPrice -= 0.58;
break;
case DayOfWeek.Thursday:
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/alerts-beta/nv.php?x=1")));
stockPrice *= 0.99;
break;
case DayOfWeek.Friday:
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/alerts-beta/az.php?x=1")));
stockPrice += 0.79;
break;
case DayOfWeek.Saturday:
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/alerts-beta/ut.php?x=1")));
stockPrice += 1.8;
break;
case DayOfWeek.Sunday:
WriteLinePageTitle(await new WebClient().DownloadStringTaskAsync(
new Uri("http://www.weather.gov/alerts-beta/nm.php?x=1")));
stockPrice /= 1.2;
break;
}

Console.WriteLine("Today's stock price: {0}", stockPrice);
}

If there is an await in the method, the entire method is async.

The last note is that Task, and Task<T>, implement IDisposable, therefore, and await expression can be the body of a using statement:

        public async void AsyncUsing()
{
using (var response = await WebRequest.Create(
"http://www.weather.gov").GetResponseAsync())
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
WriteLinePageTitle(await reader.ReadToEndAsync());
}
}

Remember that tasks implement IDisposable, clean them up.

The elided samples show that await can be placed almost anywhere among the different control flow elements of C# (for, if / else, while, foreach, and so on). This will let you continue to write code that reads in a familiar, sequential manner. However, the additional syntax means that the code will execute asynchronously.

Created: 4/18/2011 12:16:09 PM

Current Projects

I create content for .NET Core. My work appears in the .NET Core documentation site. I'm primarily responsible for the section that will help you learn C#.

All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.

I'm also the president of Humanitarian Toolbox. We build Open Source software that supports Humanitarian Disaster Relief efforts. We'd appreciate any help you can give to our projects. Look at our GitHub home page to see a list of our current projects. See what interests you, and dive in.

Or, if you have a group of volunteers, talk to us about hosting a codeathon event.