Skip to content

Async Await in C#

Become a developer with our complete learning paths
Become a developer with our complete learning paths

Welcome back to a new C# tutorial where we will learn about parallel programming in C# using the async and await keywords !.

Even if you are not a multitasker believe me multitasking in programming is VERY important : ).

So let’s jump right into it!.

The Aysnc and Await Keywords

First, we need to learn about a couple of keywords in C#.

The async modifier is used to specify that a method is asynchronous.

The await operator suspends evaluation of the async method until the asynchronous operation completes.

Async tasks can be used when we have operations that do not depend on each other. For example, load a file locally and download a file online. This scenario is similar to when you have an app that will load the user data locally and download some updates at the same time.

Preparing our Project

So let’s consider the following file :

dog

░░░░░░░░░▄░░░░░░░░░░░░░░▄░░░░
░░░░░░░░▌▒█░░░░░░░░░░░▄▀▒▌░░░
░░░░░░░░▌▒▒█░░░░░░░░▄▀▒▒▒▐░░░
░░░░░░░▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐░░░
░░░░░▄▄▀▒░▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐░░░
░░░▄▀▒▒▒░░░▒▒▒░░░▒▒▒▀██▀▒▌░░░ 
░░▐▒▒▒▄▄▒▒▒▒░░░▒▒▒▒▒▒▒▀▄▒▒▌░░
░░▌░░▌█▀▒▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐░░
░▐░░░▒▒▒▒▒▒▒▒▌██▀▒▒░░░▒▒▒▀▄▌░
░▌░▒▄██▄▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▌░
▀▒▀▐▄█▄█▌▄░▀▒▒░░░░░░░░░░▒▒▒▐░
▐▒▒▐▀▐▀▒░▄▄▒▄▒▒▒▒▒▒░▒░▒░▒▒▒▒▌
▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒▒▒░▒░▒░▒▒▐░
░▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒░▒░▒░▒░▒▒▒▌░
░▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▒▄▒▒▐░░
░░▀▄▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▄▒▒▒▒▌░░
░░░░▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀░░░
░░░░░░▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀░░░░░
░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▀▀░░░░░░░░

Let’s place it in the same directory of our exe that we get from building our console app.

 

This way we don’t need to use the absolute path of the file to access it.

IO Bound Async Operation

First, let’s add the IO namespace to use the File class in C#.

using System.IO;

The file class contains an async method called ReadAllTextAync which we can await to get the text file content as a string.

So let’s add the tasks namespace to write async tasks.

using System.Threading.Tasks;

Now let’s define a new async method that will await the task of loading a file locally.

static async Task SummonDogLocally() {

      Console.WriteLine("1. Summoning Dog Locally ...");

      //read all the text inside the dog.txt async
      string dogText = await File.ReadAllTextAsync("dog.txt");
      
      //display the data inside the txt file
      Console.WriteLine($"2. Dog Summoned Locally \\n{dogText}");
  }

Since this is an async method, we need to return a Task. returning a task like this is equivalent to a void method since we are printing the content of the file directly inside the method.

If we would to actually return the string to the main method then we would return Task<string>, similar to the ReadAllTtextAsync method if we hover over it.

The await operator will do the magic of unwrapping the string result from the task and return it to us.

Network Bound Async Operation

Cool, now let’s write our second task which is to load a string from a URL online.

Let’s add the HTTP namespace so we can send HTTP requests.

using System.Net.Http;

an HTTP client object contains a method called GetStringAsync which is an async method that will read the content of a webpage as a string, so let’s use it : ).

static async Task SummonDogFromURL(string URL) //A Task return type will eventually yield a void
{
    Console.WriteLine("1. Summoning Dog from URL ...");
    //creating a new httpClient object inside a using clause, this will make sure that the httpClient object is disposed after we are done with it
    using (var httpClient = new HttpClient()) {
        //get the data from our URL in an async manner and store the results in a string
        string result = await httpClient.GetStringAsync(URL); //execution pauses here while awaiting GetStringAsync to complete
 
        //From this line and below, the execution will resume once the above awaitable is done
        //using await keyword, it will do the magic of unwrapping the Task<string> into string (result variable)
        Console.WriteLine($"2. Dog Summoned from URL \\n{result}");
    } 
}

 

Now in the main method let’s start both of these tasks at the same time and see which of them finishes first.

First, let’s define a string called URL which is the link to a file on a GitHub repo I created.

Link to the text file on GitHub

https://raw.githubusercontent.com/l3oxer/Doggo/main/README.md

string URL = "https://raw.githubusercontent.com/l3oxer/Doggo/main/README.md";

Next, let’s define a new list of tasks that will take both of our async methods as elements since our async methods are returning a task.

//define a list of tasks that we want to do in an async manner
var tasks = new List<Task> { SummonDogLocally(), SummonDogFromURL(URL) };

 

WhenAll

 

Now before we display that both of these tasks are done we need to await for them to be done inside the main method, this can be achieved using a static method from the Tasks class called WhenAll

await Task.WhenAll(tasks);

Since we are awaiting a task in the main method, we need to mark it as an async.

//here we marked the main as an async method
  static async Task Main(string[] args) {
      //the url from the github repo that contains the data we want to download
      string URL = "https://raw.githubusercontent.com/l3oxer/Doggo/main/README.md";
      //define a list of tasks that we want to do in an async manner
      var tasks = new List<Task> { SummonDogLocally(), SummonDogFromURL(URL) };
      //wait until all tasks are done
      await Task.WhenAll(tasks);
			//pause
      Console.ReadLine();
  }

 

Actually, before we display the tasks are done message let’s also display the time it took to finish both of these tasks at the same time using an instance of the Stopwatch class.

First, let’s add the Diagnostics namespace.

using System.Diagnostics;

Then before we start the tasks we will start the stopwatch and after the tasks are done we are going to display the time.

//here we marked the main as an async method
static async Task Main(string[] args) {
    //the url from the github repo that contains the data we want to download
    string URL = "https://raw.githubusercontent.com/l3oxer/Doggo/main/README.md";
    //defining a new stopwatch to count the time span for the execution 
    Stopwatch sw = new Stopwatch();
    //start counting
    sw.Start();
    //define a list of tasks that we want to do in an async manner
    var tasks = new List<Task> { SummonDogLocally(), SummonDogFromURL(URL) };
    //wait until all tasks are done
    await Task.WhenAll(tasks);
    //stop the timmer
    sw.Stop();
    //display the time
    Console.WriteLine("We are done here.... " + sw.Elapsed.TotalSeconds);
		//pause
    Console.ReadLine();
}

 

Now let’s run the application several times.

 

 

If your internet is slow enough you will see the dog summoned from the URL appear after the dog summoned from the local file.

Just to prove that both of these tasks are running at the same time let’s slow down the SummonDogLocally Method using Thread.Sleep().

So first let’s import the Threading namespace.

using System.Threading;

And in the SummonDogLocally method, we will wait for a full second before we finish the task.

static async Task SummonDogLocally() {

    Console.WriteLine("1. Summoning Dog Locally ...");

    //read all the text inside the dog.txt async
    string dogText = await File.ReadAllTextAsync("dog.txt");

    //sleep for 1 second just to verify that both tasks run at the same time
    Thread.Sleep(1000);
    //display the data inside the txt file
    Console.WriteLine($"2. Dog Summoned Locally \\n{dogText}");
}

Now let’s run the app again.

 

Now we can see that the dog from the URL appeared before the dog from the file!.

So this is how async and await are used in C#, I really hope that you enjoyed this video. As a task for you try to write the same program synchronously without the await and async keywords and use the Stopwatch class to measure the time and compare it with the async approach : ).

Lecture Code

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
 
namespace AsyncAwaitLecture{
    class Program {
 
        //here we marked the main as an async method
        static async Task Main(string[] args) {
            //the url from the github repo that contains the data we want to download
            string URL = "https://raw.githubusercontent.com/l3oxer/Doggo/main/README.md";
            //defining a new stopwatch to count the time span for the execution 
            Stopwatch sw = new Stopwatch();
            //start counting
            sw.Start();
            //define a list of tasks that we want to do in an async manner
            var tasks = new List<Task> { SummonDogLocally(), SummonDogFromURL(URL) };
            //wait until all tasks are done
            await Task.WhenAll(tasks);
            //stop the timmer
            sw.Stop();
            //display the time
            Console.WriteLine("We are done here.... " + sw.Elapsed.TotalSeconds);
            Console.ReadLine();
        }
 
        static async Task SummonDogFromURL(string URL) //A Task return type will eventually yield a void
        {
            Console.WriteLine("1. Summoning Dog from URL ...");
            //creating a new httpClient object inside a using clause, this will make sure that the httpClient object is disposed after we are done with it
            using (var httpClient = new HttpClient()) {
                //get the data from our URL in an async manner and store the results in a string
                string result = await httpClient.GetStringAsync(URL); //execution pauses here while awaiting GetStringAsync to complete
 
                //From this line and below, the execution will resume once the above awaitable is done
                //using await keyword, it will do the magic of unwrapping the Task<string> into string (result variable)
                Console.WriteLine($"2. Dog Summoned from URL \\n{result}");
            } // we are awaiting the Async Method GetStringAsync
        }
 
        static async Task SummonDogLocally() {
 
            Console.WriteLine("1. Summoning Dog Locally ...");
 
            //read all the text inside the dog.txt async
            string dogText = await File.ReadAllTextAsync("dog.txt");
            //sleep for 1 second just to verify that both tasks run at the same time
            Thread.Sleep(1000);
            //display the data inside the txt file
            Console.WriteLine($"2. Dog Summoned Locally \\n{dogText}");
        }
    }
}

 

Lost in coding? Discover our Learning Paths!
Lost in coding? Discover our Learning Paths!
Tired of being just an average developer?
Stop wasting your time and learn coding the right (and easy) way!
Tired of being just an average developer?
Stop wasting your time and learn coding the right (and easy) way!
Enter your email and we will send you the PDF guide:
Enter your email and we will send you the PDF guide