0

Let me quote the code snippet and its relevant explanation from Async and Await article written by Stephen Cleary as follows.

public async Task DoSomethingAsync()
{
     // In the Real World, we would actually do something...
     // For this example, we're just going to (asynchronously) wait 100ms.
     await Task.Delay(100);
}

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).

If “await” sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method.

To accommodate more general case, let me define the code snippet as follows.

public async Task DoSomethingAsync()
{
  AAA();      
  await BBBAsync;
  CCC();
}

where BBBAsync is the only "awaitable" operation or method.

For the sake of simplicity, let's assume we execute DoSomethingAsync from within the main app thread. So in my understanding,

  • AAA() will always be executed synchronously.
  • BBBAsync() will always be executed asynchronously no matter how DoSomethingAsync() is invoked.
  • CCC() may be executed either asynchronously or synchronously depending on the result of the await operator determines.

It seems to me, the await operator always find the awaitable has not been completed yet because await always comes before BBBAsync() starts.

Question

When does await examine the awaitable?

Second Person Shooter
  • 14,188
  • 21
  • 90
  • 165

1 Answers1

4

It sounds like you think async makes method asynchronous which is not the case. Async only changes how code for the method is generated, but by itself it does not add any asynchronous steps nor run the method on another thread. See many questions like What is the difference between asynchronous programming and multithreading?.

await examines awaitable at the same time irrespective whether it completed yet or not - method like BBBAsync() will eventually return Task that may be either completed or not, but method will return.

Here is an example of task completing synchronously:

 async Task BBBAsync() // ignore CS1998 as we don't want `await` inside
 {
      return;
 }

 // alternatively the same behavior just returning completed Task
 // with FromResult or Task.CompletedTask for 4.6 and later
 Task BB2Async() 
 { 
      return Task.FromResult(0);
 }

You can await such task just fine, but this BBBAsync will complete synchronously and await will pick synchronous branch to continue.


More realistic sample would be returning recently cached value synchronously. This code will generate full state machine for asynchronous execution, but every other call will return synchronously thus making corresponding await to continue synchronously too.

 async Task<int> GetValueAsync()
 {
      if (canReturnCached)
      {
         canReturnCached = false;
         return cachedValue;
      }

      cachedValue = await SlowRemoteCallForValueAsync();
      canReturnCached = true;
      return cachedValue;
  }
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • 2
    Your example should be either `return;` with `async` in the definition or `return Task.FromResult(0);` (or `Task.CompletedTask` for 4.6 and later) without `async`. – Jon Hanna Oct 25 '17 at 23:23