Async / Await
async/await is a compiler transformation that lets you write non-blocking I/O code in a linear, readable style. When you await an incomplete Task, the compiler transforms your method into an IAsyncStateMachine — a state machine that suspends at the await point, releases the current thread back to the thread pool, and schedules a continuation to run when the awaited operation completes.
The key insight is that await does not create threads. It is a mechanism to release a thread while waiting for I/O. On an ASP.NET Core server, this means a single thread can handle thousands of concurrent requests — each suspended at an await, consuming no CPU.
async void is the senior trap: it returns nothing, so exceptions cannot be caught by the caller. The only valid use is event handlers. ConfigureAwait(false) skips capturing the SynchronizationContext on resume — mandatory in library code to prevent deadlocks in frameworks that have a single-threaded context.
Every async method should accept a CancellationToken and pass it to all I/O calls.
public async Task<Order> GetOrderAsync(int id, CancellationToken ct)
{
var row = await _db.Orders
.AsNoTracking()
.FirstOrDefaultAsync(o => o.Id == id, ct);
if (row is null) throw new NotFoundException(id);
ct.ThrowIfCancellationRequested();
return _mapper.Map<Order>(row);
}