Heads up! Async methods might be executing sooner than you think


Heads up! Async methods might be executing sooner than you think

When calling async methods, we usually do so by including the “await” keyword, in order to wait for its end before we proceed. Sometimes we might not really care when it ends, or we maybe don’t even want to start it right away. There are many ways to do so, but some of them might fool us.

Suppose we want to prepare a task that populates a list, clear the list, and then run the task. Depending on your familiarity with Tasks, you might end up with an approach similar to this:

var populateTask = PopulateItemsAsync();

ClearList();

await populateTask;

void ClearList()
{
    Console.WriteLine("Clearing");    
    Thread.Sleep(2000);
    Console.WriteLine("Finished clearing");
}

async Task PopulateItemsAsync()
{
    Console.WriteLine("Populating");
    await Task.Delay(500);
    Console.WriteLine("Finished populating");
}

The result will be the following:

Populating
Clearing
Finished populating
Finished clearing

Why is that? Because even though “PopulateItemsAsync()” returns a Task, it has already started. This might be confusing if you are used to creating Tasks and starting them later. But in this case, the returned task has already begun. You might want to try to fix it with the following approach:

var populateTask = new Task(async () => await PopulateItemsAsync());

ClearList();

populateTask.Start();

The syntax might not make it obvious, but you are falling into another trap, the async void trap. Basically, it means that your task will be fire-and-forget, so you will never know what happened to your task, such as an exception or when it has finished. Because we cannot know when the task has actually finished, the output will be the following:

Clearing
Finished clearing
Populating

So how do we fix this? In the case of this code, there’s nothing stopping us from simply moving the awaiting for the population after the cleaning has been done. So let’s stick to the KISS principle and use the following code:

ClearList();

await PopulateItemsAsync();

With that, we will get the expected output, which is:

Clearing
Finished clearing
Populating
Finished populating