Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Awaiting completed tasks in async methods

When we call an async method we get a wrapper task returned.

public async Task ExecuteActionAsync() {
  // no await called
}
public async Task ExecuteActionAwaitAsync() {
  // await called on a completed
  await Task.CompletedTask;
}
public async Task MyMethod(){
  await ExecuteActionAsync();
  await ExecuteActionAwaitAsync();
}

If I never use the await keyword within a function (ExecuteActionAsync) or all tasks awaited in a function (ExecuteActionAwaitAsync) are already completed by the time we await them, do I have a guarantee that the task returned by calling these methods will be in the completed state? And recursively, the task returned by MyMethod will also be in the completed state as it meets the criteria mentioned earlier (awaiting completed tasks). I believe I read about an optimization which handles this situation, but I cannot find it anywhere right now.

Bonus question: does calling Task.WhenAll on completed tasks return a completed task?

like image 697
pikausp Avatar asked Feb 13 '26 16:02

pikausp


1 Answers

This can easily be tested:

        var t1 = ExecuteActionAsync();
        Console.WriteLine($"Returned task: {t1.IsCompleted}");
        await t1;
        Console.WriteLine($"Awaited task: {t1.IsCompleted}\n");

        var t2 = MyMethod();
        Console.WriteLine($"Returned task: {t2.IsCompleted}");
        await t2;
        Console.WriteLine($"Awaited task: {t2.IsCompleted}\n");

        t1 = ExecuteActionAsync();
        t2 = MyMethod();

        var t3 = Task.WhenAll(new []{t1,t2 });
        Console.WriteLine($"Task from WhenAll: {t3.IsCompleted}");

Result:

Returned task: True
Awaited task: True

Returned task: True
Awaited task: True

Task from WhenAll: True

So: Yes, if the async method runs synchronously, the returned Task is completed. And yes, the Task returned from WhenAll passing async methods that run synchronously is also completed.

Stephen Cleary wrote a usefull blog on this subject. Specifically the following quote is relevant:

The beginning of an async method is executed just like any other method. That is, it runs synchronously until it hits an “await” (or throws an exception).

The “await” keyword is where things can get asynchronous. Await is like a unary operator: it takes a single argument, an awaitable (an “awaitable” is an asynchronous operation). Await examines that awaitable to see if it has already completed; if the awaitable has already completed, then the method just continues running (synchronously, just like a regular method).

Approaching the question form the specifications side:

  • an async method must return an 'awaitable' type (e.g. 'Task'), which must have an 'IsCompleted' property
  • "the purpose of the IsCompleted property is to determine if the task is already complete. If so, there is no need to suspend evaluation."

From these statements, it can be concluded that an async method that runs synchronously, will indeed return a completed Task.

Even if an exception is thrown while executing the async method, the returned Task will be completed ('IsCompleted' will return 'true' though 'IsCompletedSuccessfully' will return 'false').

like image 128
Johan Donne Avatar answered Feb 16 '26 04:02

Johan Donne



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!