It has been a long time since .NET version 4.5 was released. To refresh our memory, that happened on August 15th, 2012. Yes, six years ago. Feel old yet? Well, it was not my intention to bum you out but to remind you about some of the highlights of .NET release. One of the main features that this version brought was asynchronous programming using async
/await
methods. Basically, the guys from Microsoft made the compiler do work that developers used to do by keeping the logical structure that resembles synchronous code.
You see, back then, Windows Phone was still a thing, and making applications for these platforms had certain limitations. The main one was that Windows Phone, unlike desktop applications, introduced hard limit in which no method could block for more than 50ms. This, in turn, meant that there was no more blocking of the UI for the developers, which led to the necessity for some sort of asynchronicity in the code. .NET 4.5 represents the response to this necessity.
So, why am I writing about something that happened more than half a decade ago? Well, I noticed that even though this is an old topic, there are still a lot of engineers that struggle with the concept. To quote Mike James from iProgrammer:
Often, the programmer is fully aware that what they are doing is object oriented but only vaguely aware that they are writing asynchronous code.
That is why I will try to sum up the most important parts of this style of programming in a few blogs posts. Also, we will try to identify situations in which we should use this programming style, as well as the situations when we should avoid it. So, let’s dive right into it!
Motivation and Use Cases
Essentially, this style of programming is applicable to anywhere you need the ability to do something while waiting for something else to be complete. Years of usage of this tool have actually given us the ability to realize where we should use it. It is great for user experience, and in general for event-based systems, e.g., CPU-based parallelism or working with files.
Any activity that is potentially blocking, such as access to the web, is a good candidate for this approach. If any of these blocking activities end up in a synchronous process, the entire application is blocked. On the other hand, if this activity is a part of an asynchronous process, the application can continue working with other tasks until the activity is done.
Of course, you don’t need to abuse this ability. For example, we shouldn’t asynchronously update records that are dependent. It is generally a bad idea and our data will get out of sync very quickly. Apart from that, this concept is sometimes overused. For example, if we are working on some simple actions and operations we should consider more orthodox approaches. Using asynchronous concepts in these cases may, in fact, cause more overhead than benefits.
Async/Await and Return Types
The asynchronous mechanism is implemented in .NET by using async
/await
keywords in our code. We use the async
operator to mark our methods as asynchronous. Only methods with this operator methods can use await
operator in them. Await
operator, on the other hand, is telling to the compiler that async
method in which it has been used can’t continue past that point until the awaited asynchronous task is complete. Basically, it suspends the execution of the method until awaited task is done. Methods that are marked with async
operator also can be called with await
operator from the other methods.
Another important thing to mention is that async
methods must return Task class or Task class. This is because in this method, await
operator is applied to the Task
that is returned from another async
method. To sum it up, the asynchronous mechanism is in .NET implemented like this:
- We use the
async
operator to mark the methods that we want to be asynchronous. These methods must return Task class or Task class. - When we are calling methods that are marked with the
async
operator, we use await
operator. This operator will run the task that asynchronous method returned.
How does that look into the code? Take a look at this example WebAccess.cs class:
public class WebAccess
{
public async Task<int> AccessRubiksCodeAsync()
{
HttpClient client = new HttpClient();
var getContent = client.GetStringAsync("http://rubikscode.net");
LogToConsole("Yay!");
string content = await getContent;
return content.Length;
}
private void LogToConsole(string message)
{
Console.WriteLine("At the moment I am actually listening to the new NIN song...");
Console.WriteLine("It is pretty cool...like something of David Bowie's - Blackstar.");
Console.WriteLine("message");
}
}
The goal of this class is to access this website in a non-blocking manner and to get the length of the response body, for which
AccessRubiksCodeAsync
me
thod is used. We’ve put Async
suffix at the end of the name of the function, which is the standard naming convention for asynchronous methods. AccessRubiksCodeAsync
method is calling some operations that are synchronous, as well, like the function LogToConsole
. It is important to realize that GetStringAsync method is asynchronous too and it returns Task
. This Tas<wbr />k
is later run by the await
operator.
Notice how we attained the structure of the synchronous code, which is awesome. Even though we are calling await
operator and essentially running asynchronous task, our code looks pretty neat and simple. This is one of the reasons why async
/await
mechanism gained a lot of popularity.
So, what happens if GetStringAsync
method takes too long to answer? Here is how the workflow goes:
In the first step (marked with 1), AccessRubiksCodeAsync
method creates an instance of the HttpClient
and calls GetStringAsync
method of this class. The goal is to download the content of the website in a form of a string
. If something unexpected happens that blocks GetStringAsync
method, e.g., the website is taking too long to download, this method yields control to its caller. That is how it avoids blocking resources. Apart from that, this method returns Task
, which AccessRubiksCodeAsync
assigns to the variable getContent
. Later in the code, this variable is used in combination with await
operator.
Now, since we haven’t used await operator on getContent
yet, AccessRubik<wbr />sCodeAsync
can continue with other operations that are not depending on the result of getContent
task. So, the LogToConsole
method can be run in a synchronous manner (step 2). This means that this method will take control, do its job and only then give control back to AccessRubiksCodeAsync
method.
After that, this method is calling getContent
with await
operator (step 3). This means that at this moment, this method requires the result from GetStringAsync
method. If this GetStringAsync
is still not ready, AccessRubiksCodeAsync
is suspending its progress and returns control to its caller. Of course, bit benefit of having this kind flow is that we “gave some time” to GetStringAsync
method, and meanwhile, we have run synchronous parts of the code. When content is downloaded, its length is returned as a result (step 4).
Unit Testing Asynchronous Methods
Unit testing is actually one great example of how async
methods are initiated. In this example, I’ve used xUnit, but async
/await
mechanism is supported in other unit testing frameworks like NUnit and MSTests. If you want to install xUnit to your project, enter these commands in Package Manager Console:
Install-Package xunit
Install-Package xunit.runner.console
Install-Package xunit.runner.visualstudio
Update-Package
Ok, this should get you up to speed with xUnit. Now let’s take a look at our test class for WebAccess
class – WebAcces<wbr />sTests
.
public class WebAccessTests
{
[Fact]
public async Task AccessRubiksCode_NoCondition_ExpectedResult()
{
var webAccess = new WebAccess();
var result = await webAccess.AccessRubiksCodeAsync();
Assert.NotEqual(0, result);
}
}
Now, let’s analyze the important parts of this test. Firstly, notice how our test method is marked with public async Task
, unlike the standard public void
. This is done like this because in our test method, we are actually calling AccessRubiksCodeAsync
method of WebAccess
class with await
operator. We could skip this and call this method without await
operator, and keep the public void
notion, but this will not end up in the behavior we expected.
What will happen if we do this – call the async
method without await
operator? Well, in that case, this async
method will be fired separately, and in the meantime, the calling method will continue executing. Since we didn’t await
it, you’ll never know whether and when it has completed. This can be used as an advantage in certain situations of course, but it is important to know how this mechanism is working.
Apart from that, there are no crucial changes into test structure. We are first creating an instance of WebAccess
class in “arrange” phase. In “act” phase, we are using await
operator to initiate AccessRubiksCodeAsync
method and retrieve the result. Finally, in “assert
” phase, we are checking the validity of the result.
Conclusion
Asynchronous programming is a somewhat standard functionality of .NET for a number of years. Still, sometimes I get the notion that less experienced programmers don’t quite get the point of it. Especially the ones that don’t have experience with some other technologies where this mechanism is directly used in the technology itself, like Node.js. Or they are familiar with it and try to use it in every situation.
In this article, I tried to explain how asynchronous programming is done in the .NET world from the high-level. In next few articles, we will go deeper into the subject and cover some other aspects of this style, as well.
Thank you for reading!