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
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
-
Asynchronous I/O — Wikipedia, on non-blocking I/O and event-loop concurrency that keeps one thread handling many in-flight operations. ↩ ↩2 ↩3