Explain `async` and `await` keywords in C#.

4 minintermediate.NETasync-awaitconcurrency

Quick Answer

`async` marks a method as asynchronous so it can use `await`; `await` suspends the method until the awaited task completes and returns control to the caller without blocking the thread. When the task finishes, execution resumes after the await point. Async methods should return `Task`/`Task<T>` (avoid `async void` except for event handlers) and are named with an `Async` suffix by convention.

Detailed Answer

async is a modifier that marks a method as asynchronous, indicating it can contain asynchronous operations.

await is an operator that suspends the execution of an async method until the awaited task completes, without blocking the thread.

How it works:

  • When await is encountered, the method returns control to its caller
  • The thread is freed to do other work
  • When the awaited task completes, execution resumes from where it left off

Example:

// Basic async/await example
public class DataService
{
    // Async method must return Task, Task, or void (avoid void except for event handlers)
    public async Task GetDataAsync()
    {
        // Simulate a network call or database operation
        await Task.Delay(2000); // Non-blocking delay
        return "Data retrieved successfully";
    }
    
    public async Task CalculateAsync(int x, int y)
    {
        // Simulate CPU-intensive work
        await Task.Run(() =>
        {
            Thread.Sleep(1000);
        });
        
        return x + y;
    }
}

// Calling async methods
public class Program
{
    public static async Task Main(string[] args)
    {
        var service = new DataService();
        
        Console.WriteLine("Starting async operation...");
        
        // await suspends execution until GetDataAsync completes
        string result = await service.GetDataAsync();
        Console.WriteLine(result);
        
        // Multiple async operations
        int sum = await service.CalculateAsync(5, 10);
        Console.WriteLine($"Sum: {sum}");
    }
}

// Real-world example: Fetching data from an API
public class WeatherService
{
    private readonly HttpClient httpClient = new HttpClient();
    
    public async Task GetWeatherAsync(string city)
    {
        try
        {
            Console.WriteLine($"Fetching weather for {city}...");
            
            // await makes the HTTP call non-blocking
            string response = await httpClient.GetStringAsync(
                $"https://api.weather.com/forecast?city={city}"
            );
            
            Console.WriteLine("Weather data received");
            return response;
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            return null;
        }
    }
    
    // Processing multiple cities concurrently
    public async Task<List> GetWeatherForMultipleCitiesAsync(List cities)
    {
        var tasks = cities.Select(city => GetWeatherAsync(city));
        
        // Wait for all tasks to complete
        string[] results = await Task.WhenAll(tasks);
        
        return results.ToList();
    }
}

// Usage
var weatherService = new WeatherService();
var cities = new List { "New York", "London", "Tokyo" };
var weatherData = await weatherService.GetWeatherForMultipleCitiesAsync(cities);

Key Points:

  • async methods should be named with the Async suffix by convention
  • await can only be used inside async methods
  • async void should be avoided (except for event handlers) - use async Task instead
  • Exception handling works naturally with try/catch blocks