Asynchronous Programming With Async And Await

Introduction 

 
Await & Async was built on the Task Parallel Library (TPL) which was introduced in the .NET Framework 4.
 
Their purpose is to enable asynchronous programming.
 
When you download some files on your mobile phone, your phone doesn't hang until it finishes downloading those files. You can still play Youtube videos or listen to Spotify. This is possible because the OS executes these tasks asynchronously. 
 
In order to understand asynchronous programming, we need to have an example that includes a series of instructions.
 
Take a real-life example, let's make a pizza. Here is a list of steps involved in this process:
  1. Make and knead the pizza dough
  2. Prepare toppings: sliced chicken, sliced tomato 
  3. Flatten a dough ball, then stretch and round it 
  4. Spread with tomato sauce and sprinkle with toppings
  5. Slide the pizza onto the pizza stone in the oven
  6. Bake the pizza in the 475°F (245°C) oven, for about 10-15 minutes
Now, in order to make a delicious pizza, you don't have to follow the exact sequence of orders.
 
For example, you can make a dough, then preheat the stone for step 5, so that stone will be ready to bake pizza by the time you're done with other actions,  and while the stone is heating you can prepare toppings such as cutting a tomato or taking oregano & chili-flakes out of kitchen cabinet.

What you just did is execute the series of tasks asynchronously. You didn't follow the exact order as specified above.
 
We started to pre-head pizza stone after step 1, then the pizza stove will be running in the background without requiring your attention. Furthermore, we didn't wait until the pizza stone is completely heated. Rather, we moved forward with step 2 which is preparing toppings.

Here, the resource we are utilizing is time, we made some task run in the background. In the same way, in Computer Science we do asynchronous programming to utilizing resources such as Processor, RAM, etc. We are the character who makes the pizza, the same way in programming has Threads are which executes difference processes.

Let's code this out, Shall we? Because who understands theory?
 
We need some basic classes... I am not decorating classes, as our main focus is to understand asynchronous programming. 
  1.    class Dough  
  2.    {  
  3.        public void AddToppings(Toppings topping)  
  4.        {  
  5.            Console.WriteLine("Spread with tomato sauce and sprinkle with toppings");  
  6.        }  
  7.    }  
  8.   
  9.    class Toppings  
  10.    {  
  11.        public string NameOfTopping { getset; }  
  12.    }  
  13.   
  14.    class PizzaStone {    }  
  15.   
  16.    class BakeThePizza {   } 
Now our actions: functions in programming:
  1.      private static Dough MakeDough()  
  2.      {  
  3.          Console.WriteLine(" 1:  Make and knead the pizza dough, Divide the dough into two balls");  
  4.          return new Dough();  
  5.      }  
  6.   
  7.      private static Toppings PrepareToppings(Toppings topping)  
  8.      {  
  9.          Console.WriteLine(" 6:  Preparing a Topping" + topping.NameOfTopping);  
  10.          return new Toppings();  
  11.      }  
  12.   
  13.      private static void FlattenDoughBall(Dough toast) =>  
  14.          Console.WriteLine(" 9:  Flatten dough ball, and stretch out into a round");  
  15.   
  16.      private static void AddToppings(Dough toast, Toppings topping)  
  17.      {  
  18.          toast.AddToppings(topping);  
  19.      }  
  20.   
  21.      private static PizzaStone PreheatPizzaStone()  
  22.      {  
  23.          Console.WriteLine(" 3:  Heating a stone started:");  
  24.          Console.WriteLine("-------------- Waiting for 5 seconds to Preheat the Pizza stone........----------------");  
  25.          Task.Delay(5000).Wait();  
  26.   
  27.          Console.WriteLine(" 4:  Heating a stone finished");  
  28.          return new PizzaStone();  
  29.      }  
  30.   
  31.      private static BakeThePizza BackingPizza(Dough PreparedDough, PizzaStone heatedStone)  
  32.      {  
  33.          Console.WriteLine($" 11:  Slide {PreparedDough} onto {heatedStone} in oven");  
  34.          Console.WriteLine(" 12:  Bake pizza in the 475°F (245°C) oven");  
  35.   
  36.          Console.WriteLine("--------------- Waiting for 10 seconds to taking pizza out and cut into 6 slices........------------");  
  37.          Task.Delay(10000).Wait();  
  38.   
  39.          Console.WriteLine(" 13:  Take a plate out");  
  40.          Console.WriteLine("---------------- Waiting for 3 seconds to garnish........-------------");  
  41.          Task.Delay(3000).Wait();  
  42.          Console.WriteLine(" 14:  Serve the hot pizza");  
  43.          return new BakeThePizza();  
  44.      } 
Now the chef who is making a delicious pizza. Our Main method:
  1.         static void Main(string[] args)  
  2.         {  
  3.             //Step - 1  
  4.             Dough dough = MakeDough();  
  5.             Console.WriteLine(" 2:  Dough is ready");  
  6.   
  7.             //Step - 5 but started now  
  8.             PizzaStone stone = PreheatPizzaStone();  
  9.   
  10.             //Step - 2  
  11.             Toppings prepTopping = new Toppings() { NameOfTopping = "Tomato" };  
  12.             Console.WriteLine(" 5:  Toppings are preapared");  
  13.   
  14.             //Step - 3  
  15.             PrepareToppings(prepTopping);  
  16.   
  17.             //Step - 4  
  18.             AddToppings(dough, prepTopping);  
  19.             Console.WriteLine(" 8:  Toppings are");  
  20.   
  21.             //Step - 5  
  22.             FlattenDoughBall(dough);  
  23.             Console.WriteLine(" 10:  Dough has been flattern.");  
  24.   
  25.             //Final  : Eat  
  26.             BakeThePizza bakedPizza = BackingPizza(dough, stone);  
  27.             Console.WriteLine(" 14:  Pizza is ready");  
  28.             Console.WriteLine("Eat, Sleep => Repeat!");  
  29.         } 
What will happen if we run this program? We have to wait every time there is a Task.Delay used. These tasks take time to prepare.

See the behavior yourself in the following gif:
 
Asynchronous Programming With Async And Await
 
Now, what possibly could we use to utilize time which has been wasted in waiting for those processes to finish.

We can work Asynchronously and Its simple to implement it.

First, we need to add await keyword in front of the process which consumes time

Second: Decorating method with an async modifier in its signature.

That signals to the compiler that this method contains an await statement & it contains asynchronous operations. We have 2 methods:
  1.         private static async Task<PizzaStone> PreheatPizzaStone()  
  2.         {  
  3.             Console.WriteLine(" 3:  Heating a stone started:");  
  4.             Console.WriteLine("-------------- Waiting for 5 seconds to Preheat the Pizza stone........----------------");  
  5.             await Task.Delay(5000);  
  6.   
  7.             Console.WriteLine(" 4:  Heating a stone finished");  
  8.             return new PizzaStone();  
  9.         }  
  10.   
  11.         private static async Task<BakeThePizza> BackingPizza(Dough PreparedDough, PizzaStone heatedStone)  
  12.         {  
  13.             Console.WriteLine($" 11:  Slide pizza dough onto pizza stone in oven");  
  14.             Console.WriteLine(" 12:  Bake pizza in the 475°F (245°C) oven");  
  15.   
  16.             Console.WriteLine("--------------- Waiting for 10 seconds to taking pizza out and cut into 6 slices........------------");  
  17.             await Task.Delay(10000);  
  18.   
  19.             Console.WriteLine(" 13:  Take a plate out");  
  20.             Console.WriteLine("---------------- Waiting for 3 seconds to garnish........-------------");  
  21.             Task.Delay(3000).Wait();  
  22.             Console.WriteLine(" 14:  Serve the hot pizza");  
  23.             return new BakeThePizza();  
  24.         } 
Now, these methods don't simply return an object, they return asynchronous tasks containing the object.

So you have to change the calling signature as well.
  1.         static async Task Main(string[] args)  
  2.         {  
  3.             //Step - 1  
  4.             Dough dough = MakeDough();  
  5.             Console.WriteLine(" 2:  Dough is ready");  
  6.   
  7.             //Step - 5 but started now  
  8.             Task<PizzaStone> stone = PreheatPizzaStone();  
  9.             //Step - 2  
  10.             Toppings prepTopping = new Toppings() { NameOfTopping = "Tomato" };  
  11.             Console.WriteLine(" 5:  Toppings are preapared");  
  12.   
  13.             //Step - 3  
  14.             PrepareToppings(prepTopping);  
  15.   
  16.             //Step - 4  
  17.             AddToppings(dough, prepTopping);  
  18.             Console.WriteLine(" 8:  Toppings are");  
  19.   
  20.             //Step - 5  
  21.             FlattenDoughBall(dough);  
  22.             Console.WriteLine(" 10:  Dough has been flattern.");  
  23.   
  24.             //Final  : Eat  
  25.             PizzaStone pizzaStone = await stone;  
  26.   
  27.             Task bakedPizza = BackingPizza(dough, pizzaStone);  
  28.             Console.WriteLine(" 14:  Pizza is ready");  
  29.             Console.WriteLine("Eat, Sleep => Repeat!");  
  30.         }  
  31.          
Notice that now even the main method's signature is decorated with async Task as it contains awaiting task.

You may have noticed await statement for pizzaStone has moved at the end, right before where it needed.

So the compiler starts the tasks and keeps them running in the background then it continues with other tasks & brings back PizzaStone right before when it's needed.

See the output:
 
Asynchronous Programming With Async And Await

Sometimes you might have tasks that execute at the end. To execute them asynchronously you put them into await modifier.

Here is a series of await statements at the end of the preceding code that can be improved by using methods of the Task class.

One of those APIs is WhenAll, which returns a Task that completes when all the tasks in its argument list have completed.       
  1. await Task.WhenAll(stone, bakedPizza); 
Another way is to use WhenAny, which returns a Task<Task> that completes when any of its arguments complete.

The following code shows how you could use WhenAny to await the first task to finish and then process its result.

After processing the result from the completed task, you remove that completed task from the list of tasks passed to WhenAny. 
  1. var pizzaTasks = new List<Task> { stone, bakedPizza };  
  2. while (pizzaTasks.Count > 0)  
  3. {  
  4.     Task finishedTask = await Task.WhenAny(pizzaTasks);  
  5.     if (finishedTask == stone)  
  6.     {  
  7.         Console.WriteLine("Stone ready");  
  8.     }  
  9.     else if (finishedTask == bakedPizza)  
  10.     {  
  11.         Console.WriteLine("Pizza is baked");  
  12.     }Console.WriteLine("toast is ready");  
  13.     }  
  14.     pizzaTasks.Remove(finishedTask);  
  15. }  

Conclusion 

 
I sincerely hope you enjoyed this article and that you're inspired to apply what you've learned to your own applications.

Thank you, & happy coding!
 
If you have any doubts, connect with me.
References