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

Developer FabioDeveloper Fabio
2 min read

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.

0
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.