Async/await made asynchronous code look synchronous, which is wonderful — until you hit a bug that only makes sense if you understand what is happening underneath. The good news is that the mental model is simple once a few ideas click into place.
What "asynchronous" means
Some operations are slow not because of computation but because they wait — a network request, a disk read, a timer. Synchronous code would sit idle during that wait, blocking everything. Asynchronous code instead lets the program do other useful work while the slow operation is pending, and picks up the result when it is ready. The aim is to never waste time blocked on waiting.
How await pauses without blocking
This is the crucial insight. When you await something, your function pauses at that line — but the program as a whole does not freeze. The runtime is free to handle other events, other requests, other timers, and it resumes your function once the awaited operation completes. So await gives you the readability of sequential code while preserving the non-blocking behaviour of asynchronous code.
Promises underneath
Async/await is a friendlier surface over promises — objects representing a value that will arrive later. An async function always returns a promise, and await simply unwraps one when it resolves. Understanding this explains the error handling: a rejected promise becomes a thrown error you catch with an ordinary try/catch around the await.
The accidental serialization trap
The most common async mistake is awaiting things one after another when they could run together. Awaiting three independent requests in sequence makes them take as long as all three combined; firing them off together and awaiting them as a group makes them take only as long as the slowest. If operations do not depend on each other, start them concurrently and await the collection.
- Asynchronous code does other work instead of blocking on slow waits.
- await pauses your function without freezing the program.
- Async/await is syntax over promises; rejections surface as thrown errors.
- Run independent operations concurrently rather than awaiting them in sequence.
Frequently asked questions
Does async make code run in parallel?
Not by itself. It enables concurrency — overlapping waiting time. Whether things truly run in parallel depends on the runtime, but the big win is not blocking while waiting.
How do I handle errors with async/await?
Wrap awaited calls in try/catch. A rejected promise becomes a thrown error that the catch block receives, just like synchronous error handling.