Async & Await

Async & Await

In order to understand what are async& awaitkeywords and why we need them, Let us continue the download scenario discussed in the previous topic- In Take 2, We saw that the app does not freeze during the download, but the message telling us that the download is complete pops up even before the download is actually complete! If we need the main thread or UI thread to wait at a point in code, we need to explicitly use the await keyword as shown below:

Take 3: Using await keyword before calling the DownloadFile()

private async void button1_Click(object sender, EventArgs e)
{            
    await Task.Run(DownloadFile);
    MessageBox.Show("Download complete");
}

public static void DownloadFile()
{
    //simulating delay!
    Console.Beep(1500, 5000);            
}

No change to DownloadFile() but notice that we have used async keyword!

Bingo! This time the app and the download works as expected, without blocking UI and shows the message only after the download is complete! We have used the await keyword just before the Task.Run() call. This will instruct the compiler that we need to wait before moving to the next line in code, but without blocking the UI thread.

We can place the await keyword before a Task object or a method which returns a Task object (we will see that shortly)

What is async?

When we add the keyword async to the method definition, it will enable us to use the await keyword within the method, which means we can await the method in the way we need and if we do not include the keyword await, then the method will be treated as a normal or synchronous method! async and await is the newer replacement to BackgroundWorker and other complex thread/synchronization code which had been used on windows forms and other .NET applications. It was introduced in C# 5.0.

When to use async and await ?

Usually, you want to write asynchronous code for 2 different types of purposes or needs:

I/O bound code. This is when you want to do an input/output operation, particularly, downloading big resource from network, reading a huge file or accessing a database resource.

CPU bound code. This is when you want to do a heavy in-app calculation, such as calculating and displaying the remaining distance to reach the finish line of a car racing game and so on. For every async method, the .NET framework will create a state machine to keep track of the calls and what should be done after the awaitable task has complete.

In both scenarios, your application's UI or service's responsiveness should not be blocked or affected.

Creating asynchronous methods

In the Take 3 code, we have called await on a Task.Run(). Instead of doing this everywhere this method is called, we can re-write the DownloadFile() method in asynchronous fashion as shown below:

Take 4: Making the DownloadFile() go async

private async void button1_Click(object sender, EventArgs e)
{
    await DownloadFile();
    MessageBox.Show("Download complete");
}

public static async Task DownloadFile()
{
    //simulating delay!
    await Task.Run(() => Console.Beep(1500, 5000));            
}

Notice how the DownloadFile() method is returning a Task type. You can think of a Task as if it was a datatype!

In fact, several CPU, Network or I/O heavy .NET framework methods are written in an async fashion and their name also indicates the same - The Async suffix is included at the end. Going by that convention, we could have named the above method as DownloadFileAsync() to let other developers clearly know that this method is designed to be used with async/await keywords.

Returning value from an async method

In Take 4, the DownloadFile()method did not return any value to the caller. For methods that do return a value, we could use Task<T> as a return type, where T is the specific data type returned. Let’s modify the DownloadFile()method to return a true or false based on how the download task went.

Take 5: Making the DownloadFile() return a value

private async void button1_Click(object sender, EventArgs e)
{
    bool success = await DownloadFile();
    
    if (success)
        MessageBox.Show("Download complete");
    else
        MessageBox.Show("Download failed");
}

public static async Task<bool> DownloadFile()
{
    bool success = false;
    try
    {
        //simulating delay!
        await Task.Run(() => Console.Beep(1500, 5000));
        success = true;
    }
    catch (Exception)
    {
        //code to log it and inform user
    }
    return success;
}

Additional Reading

https://www.codingame.com/playgrounds/4240/your-ultimate-async-await-tutorial-in-c/structure-of-async-await

Last updated