Introduction

In modern C# development, writing responsive and high-performance applications is very important. Whether you are building web applications, APIs, or desktop software, handling long-running operations efficiently is critical. This is where async and await in C# come into play.

Async and await help you write asynchronous code in a simple and readable way. Instead of blocking the main thread, they allow your application to continue running while waiting for tasks like database calls, file operations, or API requests.

In this article, we will explain how to use async and await in C# in simple words, understand how they work internally, and explore best practices with real-world examples.

What is Asynchronous Programming in C#?

Asynchronous programming means executing tasks without blocking the main thread.

In simple words, your application can do other work while waiting for a task to complete.

Example Without Async

public void GetData()
{
    var data = FetchDataFromServer(); // Blocks thread
    Console.WriteLine(data);
}

Here, the application waits until the data is fetched.

Example With Async

public async Task GetDataAsync()
{
    var data = await FetchDataFromServerAsync();
    Console.WriteLine(data);
}

Now, the thread is not blocked while waiting.

What is async in C#?

The async keyword is used to define a method as asynchronous.

Key Points

Example

public async Task<string> GetMessageAsync()
{
    return "Hello Async";
}

What is await in C#?

The await keyword is used to pause the execution of an async method until the awaited task is complete.

Key Points

Example

public async Task ProcessAsync()
{
    await Task.Delay(2000);
    Console.WriteLine("Task completed");
}

How async and await Work Internally

Behind the scenes, async and await use Task-based asynchronous programming.

This improves performance and responsiveness.

Return Types in Async Methods

1. Task

Used when no value is returned.

public async Task DoWorkAsync()
{
    await Task.Delay(1000);
}

2. Task

Used when a value is returned.

public async Task<int> GetNumberAsync()
{
    await Task.Delay(1000);
    return 10;
}

3. void (Avoid if Possible)

Used only for event handlers.

public async void Button_Click(object sender, EventArgs e)
{
    await Task.Delay(1000);
}

Real-World Example: Calling an API

public async Task<string> GetApiDataAsync()
{
    using (HttpClient client = new HttpClient())
    {
        var response = await client.GetAsync("https://api.example.com/data");
        return await response.Content.ReadAsStringAsync();
    }
}

This allows non-blocking API calls.

Benefits of Using Async and Await in C#

Common Mistakes to Avoid

1. Blocking Async Code

var result = GetDataAsync().Result; // Avoid

This can cause deadlocks.

2. Not Using Await

GetDataAsync(); // Missing await

Task may not complete as expected.

3. Overusing Async

Do not use async for CPU-bound tasks unnecessarily.

Async vs Parallel Programming

FeatureAsync/AwaitParallel Programming
PurposeI/O-bound tasksCPU-bound tasks
Thread UsageMinimal threadsMultiple threads
ExampleAPI calls, DB queriesData processing

When to Use Async and Await in C#

Use async and await when:

Best Practices for Async Programming

1. Use Async All the Way

Avoid mixing sync and async code.

2. Use ConfigureAwait(false) (Advanced)

Improves performance in library code.

3. Handle Exceptions Properly

try
{
    await DoWorkAsync();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

4. Avoid Async Void

Except for event handlers.

Real-World Scenario

In a web application:

Summary

Async and await in C# are powerful features that help developers write non-blocking, efficient, and scalable applications. They simplify asynchronous programming and improve application performance, especially for I/O-bound operations like API calls and database queries. By following best practices and avoiding common mistakes, you can fully utilize async and await to build modern, high-performance applications.