I don\'t understand exactly what is going on behind the scenes when I have an async action on an MVC controller especially when dealing with I/O operations. Let\'s say I have an
Under the hood the compiler performs a sleight of hand and transforms your async
\ await
code into a Task
-based code with a callback. In the most simple case:
public async Task X()
{
A();
await B();
C();
}
Gets changed into something like:
public Task X()
{
A();
return B().ContinueWith(()=>{ C(); })
}
So there's no magic - just a lot of Task
s and callbacks. For more complex code the transformations will be more complex too, but in the end the resulting code will be logically equivalent to what you wrote. If you want, you can take one of ILSpy/Reflector/JustDecompile and see for yourself what is compiled "under the hood".
ASP.NET MVC infrastructure in turn is intelligent enough to recognize if your action method is a normal one, or a Task
based one, and alter its behavior in turn. Therefore the request doesn't "disappear".
One common misconception is that everything with async
spawns another thread. In fact, it's mostly the opposite. At the end of the long chain of the async Task
methods there is normally a method which performs some asynchronous IO operation (such as reading from disk or communicating via network), which is a magical thing performed by Windows itself. For the duration of this operation, there is no thread associated with the code at all - it's effectively halted. After the operation completes however, Windows calls back and then a thread from the thread pool is assigned to continue the execution. There's a bit of framework code involved to preserve the HttpContext
of the request, but that's all.