Relatively new developers who come to asynchronous programming directly using async/await may have this confusion. To understand and appreciate the value of what async/await brings to table, one has to go slightly back in time and review how asynchronous code was written then.
Before the keywords were introduced in C#, asynchronous programming was still possible. Traditionally (.NET Framework 2.0 later), there were two patterns:
Event-based Asynchronous Pattern (EAP), where you subscribed to an event:
o.ReadCompleted += (o, e) => { // asynchronously called after ReadAsync completion };
o.ReadAsync(buffer, count); // begin the async process
Task-based Asynchronous Pattern (TAP), which is Promise like interface where you can setup completions with then and catch.
o.ReadAsync(buffer, count)
.then( (result) => { // asynchronously called after ReadAsync completion } );
So, advantage of asynchronous programming on the server is that the calling thread is not blocked on the Read, which is now an asynchronous call as against a synchronous call. The thread can do some other tasks / or serve other requests.
When I/O bound Read eventually completes, the completion delegate will be called, possibly over a different thread. This way there are more threads available which makes the service more scalable. This is advantage of asynchronous programming.
However, the two patterns above have a disadvantage for the developer that the code becomes unreadable and inside-out because it is not written sequentially.
The async/await keywords just let us keep the same advantages of asynchronous programming, while making the code readable.
Now we just write:
var result = await o.ReadAsync(buffer, count);
// here comes code that we had to previously write inside the completion delegate
// but now its more readable, and much natural to handle exceptions
So although it looks sequential to developer it is still asynchronous. The compiler converts the async method into a state machine which behaves similar to code previously written, but hides away all the complex details from the developer. Similar to the completion delegate behavior, the code written after await could also be resumed on a different thread.