Stephens answer is of course correct. Here's another way to think about it that might help.
The continuation of a hunk of code is what happens after the code completes. When you hit an await
two things happen. First, the current position in the execution becomes the continuation of the awaited task. Second, control leaves the current method and some other code runs. The other code is maybe the continuation of the first call, or maybe is something else entirely, an event handler, say.
But when the call to xmlWriter.WriteStartDocumentAsync()
has completed; what happens? Is the current execution interrupted and returned back to SaveAllAsync()
?
It is not clear what you mean by the call "completing". WriteStartDocumentAsync
starts an asynchronous write, probably on an I/O completion thread, and returns you a Task
that represents that asynchronous work. Awaiting that task does two things, like I said. First, the continuation of this task becomes the current position of the code. Second, control leaves the current method and some other code runs. In this case, whatever code called SaveAllAsync
runs the continuation of that call.
Now lets suppose that code -- the caller of SaveAllAsync
continues to run, and lets suppose further that you are in an application with a UI thread, like a Windows Forms application or a WPF application. Now we have two threads: the UI thread and an IO completion thread. The UI thread is running the caller of SaveAllAsync
, which eventually returns, and now the UI thread is just sitting there in a loop handling Windows messages to trigger event handlers.
Eventually the IO completes and the IO completion thread sends a note to the UI thread that says "you can run the continuation of this task now". If the UI thread is busy, that message gets queued up; eventually the UI thread gets to it and invokes the continuation. Control resumes after the first await
, and you enter the loop.
Now WriteStartElementAsync
is invoked. It again starts some code running that depends on something happening on the IO completion thread (presumably; how it does its work is up to it, but this is a reasonable guess), that returns a Task
representing that work, and the UI thread awaits that task. Again, the current position in the execution is signed up as the continuation of that task and control returns to the caller that invoked the first continuation -- namely, the UI thread's event processor. It continues merrily processing messages until one day the IO thread signals it and says that hey, the work you asked for is done on the IO completion thread, please invoke the continuation of this task, and so we go around the loop again...
Make sense?