Async/Await in C#: Writing Clean and Safe Asynchronous Code


Asynchrony is crucial for creating performant and responsive applications, especially when dealing with I/O, network calls, or long-running operations.
In C#, the async/await pattern has revolutionized asynchronous programming by making it much more readable and easier to write.
Why use async/await?
Prevents blocking the main thread (UI or server).
Improves server application scalability.
Makes the code more readable compared to callbacks or traditional Tasks.
How does async/await work?
async indicates that a method contains asynchronous operations.
await suspends the method execution until the Task completes, without blocking the thread.
Basic rules for using async/await
Async methods must return Task or Task<T>, or void only for event handlers.
Always use await to call asynchronous methods, to avoid blocking the thread.
Avoid using .Result or .Wait() on a Task to prevent deadlocks.
Propagate async up to the top of the call stack when possible.
Writing clean asynchronous code
Avoid async void, except for specific cases (event handlers).
Name asynchronous methods with the Async suffix (e.g., GetDataAsync).
Use ConfigureAwait(false) in libraries to avoid context issues (when you don't need to return to the original context).
Properly handle exceptions in async methods with try/catch.
Practical example
public async Task<string> GetWebPageAsync(string url)
{
using var client = new HttpClient();
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return content;
}
Be cautious about...
Deadlock: Never block asynchronous methods with .Result or .Wait() in synchronization contexts (e.g., UI).
Unhandled exceptions: Errors in async methods propagate through Tasks, so always use try/catch.
Parallelism vs. Asynchrony: Async does not mean running in parallel; it means not blocking.
Testing asynchronous code
Tests should be async Task and use await to call asynchronous methods.
Frameworks like xUnit natively support asynchronous tests.
Conclusion
Correctly using async/await significantly improves the quality and responsiveness of C# applications.
Writing clean asynchronous code requires attention to best practices and a good understanding of the model.
Subscribe to my newsletter
Read articles from Developer Fabio directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Developer Fabio
Developer Fabio
I'm a fullstack developer and my stack is includes .net, angular, reactjs, mondodb and mssql I currently work in a little tourism company, I'm not only a developer but I manage a team and customers. I love learning new things and I like the continuous comparison with other people on ideas.