Understanding and Using async and await in .NET

Deewakar Kumar
3 min readAug 3, 2024

Introduction

Asynchronous programming in .NET has become a vital skill for developers looking to write responsive applications. The async and await keywords were introduced in C# 5.0 to simplify the implementation of asynchronous operations. This guide provides a comprehensive overview of when and how to use these keywords, ensuring a detailed understanding of their usage and benefits.

When to Use async and await

1. I/O-Bound Operations

Asynchronous programming is particularly beneficial for I/O-bound operations, such as network requests, file I/O, or database queries. These operations often involve waiting for external resources, during which the CPU remains idle. Using async and await allows other tasks to execute while waiting, improving the application's responsiveness.

2. Long-Running Tasks

For tasks that can take a long time to complete, using async and await helps prevent blocking the main thread, especially in GUI applications. This ensures that the user interface remains responsive, avoiding the "frozen" effect.

3. Scalability

In server applications, asynchronous programming can improve scalability by freeing up threads to handle other requests while waiting for I/O operations to complete.

How to Use async and await

1. Defining an Asynchronous Method

An asynchronous method is defined by using the async modifier in its signature. This modifier indicates that the method contains asynchronous operations.

public async Task<int> FetchDataAsync()
{
// Simulate an asynchronous operation
await Task.Delay(1000);
return 42;
}

2. Using await to Await Tasks

The await keyword is used to asynchronously wait for the completion of a task. When the awaited task is not yet completed, control returns to the calling method, and the awaiting method resumes when the task is done.

public async Task UseFetchDataAsync()
{
int result = await FetchDataAsync();
Console.WriteLine(result);
}

The await keyword can only be used inside methods marked with the async keyword.

3. Returning Task and Task<T>

Asynchronous methods typically return Task or Task<T>. The Task type represents an operation that can be awaited but does not return a value, while Task<T> represents an operation that returns a value of type T.

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

public async Task<string> GetDataAsync()
{
await Task.Delay(500);
return "Data";
}

4. Exception Handling in Async Methods

Exceptions in asynchronous methods can be caught using try-catch blocks, just like in synchronous code. The await keyword will throw the exception encountered during the execution of the awaited task.

public async Task<string> SafeGetDataAsync()
{
try
{
await Task.Delay(500);
throw new Exception("Something went wrong!");
return "Data";
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return "Error";
}
}

Best Practices

  1. Avoid Blocking Calls: Do not use blocking calls like .Result or .Wait() in asynchronous code, as they can lead to deadlocks.
  2. Use Asynchronous Methods from the Start: If an API offers both synchronous and asynchronous methods, prefer the asynchronous ones.
  3. Beware of Context Capturing: Be cautious of the synchronization context, especially in UI applications. Use ConfigureAwait(false) to avoid capturing the context when you don't need it.

Conclusion

The async and await keywords in .NET provide a powerful way to write efficient and responsive code. Understanding when and how to use them effectively is crucial for modern application development, especially for handling I/O-bound operations, long-running tasks, and improving scalability. By following best practices and understanding the mechanics of asynchronous programming, developers can create applications that offer a smooth user experience.

--

--

Deewakar Kumar

I am a dot net developer with total experience of around 10 years. I love coding and also working on upgrading my skills on other technologies as well.