Logo

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?

  1. 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).
  2. Scalability: Handle multiple operations concurrently (especially in web or server apps) without blocking threads.
  3. Simplicity: async/await helps you write asynchronous code in a sequential style, unlike older patterns with callbacks or the Begin/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 (or Task<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.

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

  1. 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.
  2. Return Task Instead of void

    • For asynchronous methods, prefer returning a Task so the caller can await it.
    • Use async void only for event handlers (e.g., GUI button clicks) or special, top-level scenarios where a Task would not be feasible.
  3. Avoid Blocking Calls

    • Don’t combine await with blocking operations like .Result or .Wait(). Doing so often leads to performance degradation or deadlocks.
  4. 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.
  5. Cancellation Tokens

    • For long-running operations, consider supporting CancellationToken to let callers cancel pending tasks gracefully.

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:

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.

CONTRIBUTOR
TechGrind