• Skip to primary navigation
  • Skip to main content
  • Skip to footer

Codemotion Magazine

We code the future. Together

  • Discover
    • Events
    • Community
    • Partners
    • Become a partner
    • Hackathons
  • Magazine
    • Backend
    • Frontend
    • AI/ML
    • DevOps
    • Dev Life
    • Soft Skills
    • Infographics
  • Talent
    • Discover Talent
    • Jobs
    • Manifesto
  • Companies
  • For Business
    • EN
    • IT
    • ES
  • Sign in

Sergio MonteleoneDecember 3, 2019

.NET Async/Await and its catches

Backend
facebooktwitterlinkedinreddit

Since the introduction of the Task-based Asynchronous Pattern (or TAP) with .NET 4.0 programmers have enjoyed a simpler and streamlined approach to asynchronous programming in .NET, improving performance and readability of their code.

If you are already familiar with .NET TAP basic concepts, don’t miss the opportunity to learn more from an expert: Brandon Minnick, developer advocate with Microsoft, will address some in his talk “Correcting Common Async/Await Mistakes in .NET” at Codemotion Milan 2019 – check the agenda. Hurry up as tickets are still available!

Was it worth the Await?

Before TAP, .NET programmers had several alternatives to write non-blocking code:

  • Using the Asynchronous Programming Model (APM), with its Begin*, End* methods and its IAsyncResult interface;
  • The Event-based Asynchronous Pattern (EAP), which is the legacy event-based model introduce with .NET 2.0;
  • Writing code at a lower level using Threads and the related synchronization primitives (Mutex, Semaphore, etc).

All of these alternatives are somewhat cumbersome and difficult to handle compared to the Async/Await paradigm introduced with TAP. They all require a lot of boilerplate just to get things up and running, with the obvious consequence of hurting readability and maintainability.

TAP is instead very simple to grasp, as far as parallel programming can go. It is modelled around the concept of Tasks as the name implies. A Task is simply a generic asynchronous operation, some piece of code we want to run without blocking the caller or other tasks.

An async Hello World

So let’s get started with a simple “hello world” example with the Async/Await constructs. The goal of this simple program is to perform a few HTTP GET requests against a list of servers. A simple synchronous version would be:

The method getDataSync() performs an HTTP request using the HttpClient class in System.Net.Http and returns the result as a String. The method DoWork() calls the method getDataSync() for all the websites in the list.

There is an obvious problem with this implementation: throughput. Requests are performed one after the other in sequence. This hurts the overall performance since our program is blocked waiting for a request to complete before attempting to perform anything else. Moreover, if an error occurs while performing one of the GET calls, the entire sequence is interrupted. This is often an undesired behaviour, as in case of errors we want to be able to gracefully rollback or continue with the other calls.

Lastly, the main thread is also blocked for the entire time, making the program completely unresponsive. If this piece of code would be part of a desktop application, the GUI would freeze until all the requests are finally completed.

So let’s change the code to perform all the requests asynchronously and in a parallel fashion. To turn a normal method to an asynchronous one we must perform these changes:

  • Add the async keyword to its definition. This enables the usage of the await operator inside that method. The await operator will suspend the execution of the async method until the asynchronous operation represented by its operand is completed;
  • Change the return type to Task<T> where T is the return data type;
  • Respect the naming convention: an asynchronous method name should end with the word “Async”;

Here is how the getDataAsync() method looks like:

While this will execute the requests in different Tasks, they will still be in a sequence, because of the await in the foreach loop. A better approach would be to let the Tasks run in parallel and let the DoWork() method return when they have all completed their mission. This can be easily achieved with the Task.WaitAll() method. To do so, we create an array of Tasks, populate it with the Tasks returned by each call of the getDataSync() method and then pass the array to Task.WaitAll() to wait for them:

But there’s a catch

Or there is not. So far we assumed things are always going smoothly. But what happens if an exception is thrown inside one of the async methods? Let’s simulate this condition by adding a non existing URL to the list of sites to request. This will obviously make the HttpClient throw an HttpRequestException since the DNS request will fail.

As expected, an exception is thrown. The interesting fact here is that the exception is thrown by the Task.WaitAll() method, and it is a System.AggregateException. That is because the .NET framework will automatically surround our aysnc methods with try/catch blocks to let the Task awaiter handle them. The exception thrown by the HttpClient, a SocketException in this case, can be accessed as an InnerException. Let’s handle these exceptions in the DoWorkAsync() method:

What if we want to manage the exception in the getDataAsync() method instead? As for any other method, we can simply wrap the await call inside a try/catch block:

This works as expected because HttpClient.GetStringAsync() method will assign any exception that occurs during its execution to the returning Task.

Conclusions

There is a lot more to cover about asynchronous programming with .NET, considering best practices, optimization and exception handling. There are several odd cases in which exception handling may give you some headaches when dealing with async methods.

Again, if you want to learn more about it, don’t miss the talk Brandon Minnick, developer advocate with Microsoft, will give at Codemotion Milan 2019 where he will address more advanced topics and show some interesting examples. Get your ticket here!

Related Posts

Top 10 online platforms to practice Python every dev should know

Lucilla Tomassi
May 13, 2025

.NET Refactoring Series: Episode 1 — How to Approach Service Refactoring

giovanni-ferrari
April 2, 2025

Queueing Without a Queue: The PostgreSQL Hack

Puppo92
March 13, 2025

How to tell if you’ve got what it takes: let’s do a code review

Matteo Baccan
January 14, 2025
Share on:facebooktwitterlinkedinreddit

Tagged as:Codemotion Milan Dot NET

Sergio Monteleone
Software developer and the co-founder of Moga Software s.r.l., a software house based in Italy. I tend to write code for anything that has a C/C++ compiler, but don't mind using other technologies and languages. I love cats, dogs and, more in general, any lifeform when Lifeform.numLegs() <= 4.
Magically deploy a new project in 1 month
Previous Post
Nobody puts UX in the corner
Next Post

Footer

Discover

  • Events
  • Community
  • Partners
  • Become a partner
  • Hackathons

Magazine

  • Tech articles

Talent

  • Discover talent
  • Jobs

Companies

  • Discover companies

For Business

  • Codemotion for companies

About

  • About us
  • Become a contributor
  • Work with us
  • Contact us

Follow Us

© Copyright Codemotion srl Via Marsala, 29/H, 00185 Roma P.IVA 12392791005 | Privacy policy | Terms and conditions