Working with Async/Await

Tutorial 2 of 5

1. Introduction

This tutorial aims to equip you with the knowledge and skills to work with the async/await pattern in C#. By the end of this tutorial, you'll understand what async/await is, when and how to use it, and best practices to follow.

Prerequisites: Basic knowledge of C# programming language and understanding of the .NET environment.

2. Step-by-Step Guide

Async/await is a syntactic sugar in C# that makes asynchronous programming more manageable. Asynchronous programming is essential for activities that are potentially blocking, such as accessing the web or a database.

Understanding Async/Await

The async keyword indicates that the method can run asynchronously. It's a modifier to a method, which tells the compiler to setup state-machine infrastructure for you in the method.

The await keyword can only be used inside an async method and is used to suspend the execution of the method until the awaited task completes.

Best practices and tips

  • Always try to use async all the way down, i.e., once you've decided to go async, make sure all methods in the call tree are also async.
  • Avoid async void methods. They make it hard to know when the operation has completed and they don’t provide a way to catch exceptions.
  • Use Task or Task as a return type for async methods.

3. Code Examples

Example 1: Basic Async/Await

// This method returns a Task that represents a running operation
public async Task PerformOperationAsync()
{
    // Await the completion of a task
    // This will not block the current thread and return control to the caller
    await Task.Run(() =>
    {
        // Simulate a long running operation
        Thread.Sleep(2000);
    });

    Console.WriteLine("Operation Completed!");
}

In the above code snippet, we have an asynchronous method PerformOperationAsync. We use the Task.Run method to start a new task that simulates a long-running operation. We then use the await keyword to asynchronously wait for the task to complete.

Example 2: Async/Await with return value

public async Task<int> CalculateSumAsync(int a, int b)
{
    return await Task.Run(() =>
    {
        Thread.Sleep(2000);
        return a + b;
    });
}

In this example, the CalculateSumAsync method is an async method that returns a Task<int>. This means that it's an asynchronous operation that produces an int result. We use Task.Run to start a new task that simulates a long-running operation (calculating a sum), and then we return the result of the operation.

4. Summary

In this tutorial, we've covered the basics of async/await in C#. You've learned how to create asynchronous methods using the async keyword, how to use the await keyword to wait for a task to complete without blocking the current thread, and some best practices to follow when working with async/await.

For further learning, you should look into how to handle errors in async methods, how to use Task.WhenAll and Task.WhenAny, and how to cancel tasks.

5. Practice Exercises

  1. Write an async method that simulates reading a file and returns the number of lines in the file.
  2. Write an async method that downloads a web page and returns the page's content as a string.

For both exercises, remember to simulate the long-running operations using Task.Run and Thread.Sleep.

Here are the solutions:

// Solution to exercise 1
public async Task<int> CountLinesAsync()
{
    return await Task.Run(() =>
    {
        Thread.Sleep(2000);
        // Simulates reading a file and counting the lines
        return 100;
    });
}

// Solution to exercise 2
public async Task<string> DownloadPageAsync()
{
    return await Task.Run(() =>
    {
        Thread.Sleep(2000);
        // Simulates downloading a web page
        return "<html><body>Hello World!</body></html>";
    });
}

Remember, the key to mastering async/await is practice. Try to incorporate it in your next project and see how it improves your code's readability and performance.