Async / Await in C#



In C#, 'async' and 'await' are both keywords fundamental to asynchronous programming. They enable the creation of responsive applications by preventing the main thread from being blocked during long-running operations.

Asynchronous Programming in C#

The Task Asynchronous Programming (TAP) model in C# makes writing asynchronous code easier and cleaner. It lets you write code just like normal sequential code, but the difference is that tasks run in the background without blocking the main thread. This means your program can keep working on other things while waiting for a task (like downloading data or reading a file) to finish. The compiler takes care of managing the flow, so your code stays simple and easy to read.

What is Async and Await in C#

C# provides built-in support for asynchronous programming through the async and await keywords. These keywords allow developers to perform non-blocking operations, such as file handling, I/O network calls or database queries, without freezing the main thread.

Asynchronous Programming
Asynchronous programming improves application responsiveness by keeping your app active, smooth, and quick to respond, even during long operations. This is particularly true in UI-based and server-side applications, where blocking operations can break performance.

Listed here are some of the important points on asynchronous programming −

  • async and await make writing asynchronous code simple and easy to read.
  • Use the async keyword before a method, lambda, or anonymous function to mark it as asynchronous.
  • Use await inside an async method to pause the code until a task finishes.
  • An async method usually returns Task, Task<TResult>, or void (used only for event handlers).
  • While waiting for a task, the program doesn’t block the main thread - it stays responsive.

Declaration of Async and Await

In C#, we can use the async and await keywords together to define and work with asynchronous methods.

async Task MethodNameAsync() {
   await SomeAsyncOperation();
}

Explanation

  • The async keyword is added before the method declaration to mark it as asynchronous.
  • The await keyword is used inside the method to wait for another asynchronous operation to complete.
  • The code after await runs only after the awaited task is finished, keeping the main thread free.

Let's look at an example of how the program waits for "DownloadDataAsync()" to finish without blocking other code from running.

public async Task GetDataAsync() {
   // Calling just random function to show declaration.
   string data = await DownloadDataAsync();
   Console.WriteLine(data);
}

C# async and await Best Practices

Let's understand what should follow to ensure that we are performing asynchronous tasks perfectly −

  • An async method should have at least one await; otherwise, it runs like a synchronous method.
  • Use async Task for methods that do asynchronous work but don’t return a value.
  • Use async Task<TResult> for methods that return a value.
  • Avoid async void except for event handlers.
  • Don’t use blocking calls like .Result or .Wait(), as they can freeze the program or cause deadlocks.

Example: Asynchronous Program with Task.Delay()

In this example, we are using async and await with the delay method to show the message after a specific delay −

using System;
using System.Threading.Tasks;

class myProgram {
   static async Task Main() {
      Console.WriteLine("Hi Tutorialspoint your task will be completing within 2 seconds");
     
     // Delay is function to delay a task
     await Task.Delay(2000);
     Console.WriteLine("Hey task completed after 2Secs.");
   }
}

When you run this code, it will produce the following output

Hi Tutorialspoint your task will be completing within 2 seconds
Hey task completed after 2Secs.

Example: Running Multiple Tasks with async and await

In the following example, we manage the multiple tasks in a single code without blocking the other tasks using the 'async' and 'await' keywords −

using System;
using System.Threading.Tasks;

class myProgram {
   static async Task Main() {
      Console.WriteLine("Starting multiple tasks...");

      // Run multiple async tasks
      Task fetchTask = FetchDataAsync();
      Task saveTask = SaveDataAsync();
      Task notifyTask = SendNotificationAsync();

      // Wait for all tasks to complete
      await Task.WhenAll(fetchTask, saveTask, notifyTask);

      Console.WriteLine("All tasks completed successfully!");
   }

   static async Task FetchDataAsync() {
      Console.WriteLine("Fetching data...");
      await Task.Delay(2000);
      Console.WriteLine("Data fetched successfully!");
   }

   static async Task SaveDataAsync() {
      Console.WriteLine("Saving data...");
      await Task.Delay(3000);
      Console.WriteLine("Data saved successfully!");
   }

   static async Task SendNotificationAsync() {
      Console.WriteLine("Sending notification...");
      await Task.Delay(1000);
      Console.WriteLine("Notification sent!");
   }
}

Following is the output

Starting multiple tasks...
Fetching data...
Saving data...
Sending notification...
Notification sent!
Data fetched successfully!
Data saved successfully!
All tasks completed successfully!

Example: Return the Square of a Number

In the following example, we are performing an async task with a return type. Here we are returning the square of a number.

using System;
using System.Threading.Tasks;

class MyProgram {
   static async Task Main() {
      int result = await CalculateSquare(10, 2);
      Console.WriteLine("Square: " + result);
   }
   
   static async Task<int> CalculateSquare(int a, int b) {
      return await Task.Run(() => (int)Math.Pow(a, b));
   }
}

Following is the output -

Square: 100

Example: Read File Content Asynchronously

In the following example, we read a File, Asynchronously using async and await in C#

using System;
using System.IO;
using System.Threading.Tasks;

class myProgram {
   static async Task Main() {
      string path = "tutorialspoint.txt";
      string content = await ReadFileAsync(path);
      Console.WriteLine("Content of the File:\n" + content);
   }
   
   static async Task<string> ReadFileAsync(string filePath) {
      using (StreamReader reader = new StreamReader(filePath)) {
         return await reader.ReadToEndAsync();
      }
   }
}

The above code will not run on the online compiler; it will run on the local system, and you should pass the path of the "tutorialspoint.txt" file.

Conclusion

Async and await in C# make the code easy to run tasks without blocking the other thread or task. They help keep applications smooth and responsive while performing background operations.

Advertisements