Codepth.dev
Must Know

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.

Code examples

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);
}
Note: Always propagate CancellationToken. Never swallow OperationCanceledException — let it bubble.