Running until await
async and await let a method wait for slow work without holding a thread. A thread is a worker that runs code one line at a time. Thread 1 starts at line 1 and prints Start.
Line 2 calls GetAsync. The async keyword lets a method use await, but the call starts like any other: the same thread jumps in and runs it line by line. It prints Fetch.
Pausing without blocking
Task.Delay(1000) starts a timer and returns at once with a Task: an object for work that finishes later. await checks it. The task is not complete, so GetAsync pauses here and saves its place.
Pausing does not block anything. GetAsync returns to its caller at once, with an unfinished Task<string>, and line 2 stores it in job. IsCompleted is false: the string does not exist yet.
Thread 1 goes on to line 3 and prints Working while the timer is still counting. This is the point of async: other work runs during the wait instead of a thread sitting idle.
Line 4 awaits job. It is still unfinished, so Main also saves its place and lets go of the thread. Now none of your code is running, and none of it holds a thread while it waits.
Resuming
About 1000 ms later the timer completes the Delay task. That wakes GetAsync's saved rest, its continuation. A console app has no special thread to go back to, so a thread-pool thread picks it up.
A pool thread carries on in GetAsync right after the await. return "Data" ends the method, and that completes job: IsCompleted turns true and job's Result is now the string Data.
Completing job wakes Main, usually on that same pool thread. await job now gives back the string, so s = "Data", and line 5 prints it. The order was Start, Fetch, Working, Data: line 3 ran before GetAsync had finished.
The rules
Mark a method async to use await in it; it usually returns Task or Task<T>. await pauses the method, never the thread. Await tasks instead of calling .Result or .Wait(): those block a thread and can deadlock.
What happens
// step 1/10 · start
t = 0 ms Thread 1 line 1 Console.WriteLine("Start");
console Start
thread a worker that runs code one line after another
Top-level statements that use await become an async Main that returns a Task. The hidden entry point that starts it simply waits for that Task, so the console program ends only after line 5. In an app with a window, the freed thread would go back to drawing it. Which thread runs a continuation depends on the SynchronizationContext. A console app has none, so continuations run on thread-pool threads; a desktop UI resumes on its UI thread. The figure shows one pool thread; in reality Main may resume on the same or another pool thread.