Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async/Await behaviour through the stack

I'm curious about how the flow of async works across the stack. When reading about async in C#, you will continually read some version of the following:

If the task we are awaiting has not yet completed then sign up the rest of this method as the continuation of that task, and then return to your caller immediately; the task will invoke the continuation when it completes.

It's the return to your caller immediately part that confuses me. In the below example, assuming something calls MethodOneAsync(), execution hits the await in MethodOneAsync. Does it immediately return to the method that called this? If so, how does MethodTwoAsync ever get executed? Or does it continue down the stack until it detects that it's actually blocked (ie. DoDBWorkAsync()) and then yield the thread?

public static async Task MethodOneAsync()
{
    DoSomeWork();
    await MethodTwoAsync();
}

public static async Task MethodTwoAsync()
{
    DoSomeWork();
    await MethodThreeAsync();
}

public static async Task MethodThreeAsync()
{
    DoSomeWork();
    await DoDBWorkAsync();
}
like image 620
Cuthbert Avatar asked Jul 21 '26 08:07

Cuthbert


1 Answers

The part before an await in an async method is executed synchronously. That's the case for all async methods.

Let's assume that instead of await DoDBWorkAsync() we have await Task.Delay(1000).

That means MethodOneAsync starts running, executes DoSomeWork and calls MethodTwoAsync which in turn executes DoSomeWork which calls MethodThreeAsync which again executes DoSomeWork.

Then it calls Task.Delay(1000), gets back an uncompleted Task and awaits it.

That await is logically equivalent to adding a continuation and returning the task back to the caller, which is MethodTwoAsync which does the same and return a Task to the caller and so forth and so forth.

That way when the root delay Task completes all the continuations can run one after the other.


If we make your example a bit more complicated:

public static async Task MethodOneAsync()
{
    DoSomeWorkOne();
    await MethodTwoAsync();
    DoMoreWorkOne();
}

public static async Task MethodTwoAsync()
{
    DoSomeWorkTwo();
    await MethodThreeAsync();
    DoMoreWorkTwo();
}

public static async Task MethodThreeAsync()
{
    DoSomeWorkThree();
    await Task.Delay(1000);
    DoMoreWorkThree();
}

It would be logically similar to doing this with continuations:

public static Task MethodOneAsync()
{
    DoSomeWorkOne();
    DoSomeWorkTwo();
    DoSomeWorkThree();
    return Task.Delay(1000).
        ContinueWith(_ => DoMoreWorkThree()).
        ContinueWith(_ => DoMoreWorkTwo()).
        ContinueWith(_ => DoMoreWorkOne());
}
like image 168
i3arnon Avatar answered Jul 23 '26 21:07

i3arnon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!