How and when to use ‘async’ and ‘await in C#?
Below is a concise explanation of how and when to use the async
and await
keywords in C# to implement asynchronous programming. You’ll learn the key concepts, see code samples, and discover best practices for writing more responsive and efficient applications.
What are async
and await
?
async
: A modifier used on a method or lambda expression to mark it as asynchronous.await
: An operator used within an async method to suspend its execution until the awaited task completes.
Why Use Asynchronous Programming?
- Responsiveness: Free up the UI thread in desktop or mobile apps, ensuring the UI doesn’t freeze while performing long operations (e.g., network calls, file I/O).
- Scalability: Handle multiple operations concurrently (especially in web or server apps) without blocking threads.
- Simplicity:
async
/await
helps you write asynchronous code in a sequential style, unlike older patterns with callbacks or theBegin/End
pattern.
Basic Example
public async Task SomeOperationAsync() { // Simulate an I/O-bound operation (e.g., reading a file). await Task.Delay(2000); // Wait 2 seconds asynchronously Console.WriteLine("Operation completed."); }
- Key Points:
- The method signature returns a
Task
(orTask<TResult>
if returning a value). - The
await
keyword pauses execution until the awaited task finishes. - The method doesn’t block the calling thread while waiting. Instead, the thread is free to do other work, and the method resumes when the task completes.
- The method signature returns a
Using await
in Real-World Scenarios
1. I/O-Bound Calls
Network and file operations often dominate application responsiveness. Using async
/await
here prevents blocking the thread for slow I/O.
public async Task<string> FetchDataFromApiAsync(string url) { using HttpClient client = new HttpClient(); string result = await client.GetStringAsync(url); // Asynchronous HTTP request return result; }
2. Database Queries
When connecting to a database, you can leverage asynchronous APIs to ensure your application can handle multiple concurrent queries without blocking.
public async Task<List<User>> GetUsersAsync() { using var context = new MyDatabaseContext(); return await context.Users.ToListAsync(); }
3. Parallel Operations
You can run multiple tasks concurrently and await them all:
public async Task FetchAllDataAsync(string url1, string url2) { var task1 = FetchDataFromApiAsync(url1); var task2 = FetchDataFromApiAsync(url2); // Wait for both tasks to complete await Task.WhenAll(task1, task2); Console.WriteLine(task1.Result); Console.WriteLine(task2.Result); }
Best Practices
-
Use
async
All the Way- If you call an asynchronous method, ideally, you should make the calling method
async
too, propagating the asynchronous pattern up the call stack. - Avoid “sync over async” (i.e., calling
.Result
or.Wait()
) except in the entry point (e.g.,Main
) or unit tests, as it can cause deadlocks.
- If you call an asynchronous method, ideally, you should make the calling method
-
Return
Task
Instead ofvoid
- For asynchronous methods, prefer returning a
Task
so the caller canawait
it. - Use
async void
only for event handlers (e.g., GUI button clicks) or special, top-level scenarios where aTask
would not be feasible.
- For asynchronous methods, prefer returning a
-
Avoid Blocking Calls
- Don’t combine
await
with blocking operations like.Result
or.Wait()
. Doing so often leads to performance degradation or deadlocks.
- Don’t combine
-
Exception Handling
- Wrap async calls in
try-catch
when you need robust error handling. - Exceptions bubble up via the returned
Task
—the same as synchronous methods but within an async context.
- Wrap async calls in
-
Cancellation Tokens
- For long-running operations, consider supporting
CancellationToken
to let callers cancel pending tasks gracefully.
- For long-running operations, consider supporting
Strengthen Your C# and Interview Skills
Mastering async
/await
is just one aspect of modern C# development. If you’re aiming to take your coding and system design expertise to the next level, consider these resources from DesignGurus.io:
- Grokking the Coding Interview: Patterns for Coding Questions
- Grokking System Design Fundamentals (great for beginners)
- Grokking the System Design Interview (for more advanced discussions)
You can also check out the DesignGurus.io YouTube channel for free tutorials and insights on coding interviews, system design basics, and C# best practices.
Using async
/await
empowers you to write asynchronous code in a clear, linear style. Whenever you need to wait on a time-consuming I/O operation, reach for await
. It keeps your application responsive, saves resources, and fosters scalability—whether you’re building desktop apps or distributed cloud services.