If all async Task Method()
calls return Task.FromResult()
- does that execute synchronously?
I am asking this in the context of writing bUnit tests for my Blazor server app.
A big issue for bUnit is you need the rendering to complete before asserting content in the rendered page.
My async Task OnInitializedAsync()
call a lot of async services. For the unit tests I have mock services and ever service method returns a Task.FromResult()
of static data.
In this case, when inside the method I have:
_organization = await query.FirstOrDefaultAsync();
Does it build up a task an return immediately? Or does it see that the task is completed, assign the value, and continue executing?
In other words, for the test case, where there is no true async activity, does it execute synchronously and OnInitializedAsync()
returns a Task
that is completed?
I will expand a little with my answer before I talk about bUnit and will also go into simple examples of what @guru-stron wrote. For beginners, we have to explore the state machine of
async
at least to a very small degree.Scenario 1
That is in @guru-stron example the first one.
This will result to:
In regards of this ominous state-machine. Think of "await" as a method to slice your method into smaller methods. For each "await" you have one method that has the content from the last await (or beginning) to the current await. Now if you await something and you spin up the Task - you give back control to the caller. You do this process for each caller in the chain that also uses "await". If you don't await (like in my given example the first calling function) then you keep the control flow inside that methods until
await
gets called. Once thatawait
is hit your Task tries to continue (there is a lot more involved, but let's try to keep it simple). Now the most innerTask
is completed (the one withTask.Delay(1)
) - therefore we continue "like a synchronous function".So as we don't directly await in the most outer function - we have "In between Something" and then the Console.WriteLine from the most inner and so on.
The Blazor Renderer, on which bUnits Renderer ultimately is based on, behaves like that. It is literally like the most outer function. So it "sees"
OnInitializedAsync
for example. IfOnInitializedAsync
goes to a db asynchronously then exactly that process I describes kicks in - therefore the renderer is "done" even though there will be future work.Scneario 2
Now if we take the example above, but directly return a completed Task:
we get this:
I hope now that makes sense, as we never "give back" control to the caller! There "is nothing to await" (simplified).
So if you have completed Tasks inside
OnInitializedAsync
and friends, everything behaves synchronously.