Field Guide · concept

Also known as: async, async/await

Asynchronous programming runs many tasks concurrently on a single thread using an event loop: whenever a task waits on something slow, it yields control back to the loop, which runs other ready tasks until the slow thing finishes.1

eventloop run task A await waiting (I/O) task B runs
When a task hits an await, it yields to the loop, which runs another ready task until the result arrives.

How it works

Code marks slow operations with await. When a task reaches one, it suspends and hands the single thread back to the event loop, which picks another task that is ready to make progress; when the awaited operation completes, the loop resumes the original task where it left off. No operating-system thread is blocked sitting idle, so one thread can keep thousands of in-flight operations moving.1 Because there is only one thread of execution, there is no shared-memory data race between the tasks.

Trade-offs

Async excels at I/O-bound work — thousands of concurrent network connections or disk reads on a single thread — because such workloads spend most of their time waiting, not computing. It does little for CPU-bound work: a long computation still hogs the single thread and blocks every other task, since cooperative scheduling only switches at await points.1 Async code can also be harder to reason about, and a blocking call slipped into an async path stalls everything.

In practice

JavaScript (Node.js and the browser), Python (asyncio), C#, and Rust all offer async/await. For CPU-bound parallelism you reach instead for threads or lightweight tasks like goroutines, which can spread work across cores.

Sources

  1. Asynchronous I/O — Wikipedia, on non-blocking I/O and event-loop concurrency that keeps one thread handling many in-flight operations.  2 3

See also