asynchronous I/O
There are three ways to order takeout. Blocking is phoning and staying on hold, doing nothing until the food is ready. Non-blocking is calling, being told 'not ready, call back,' and repeatedly checking while you do other things. Asynchronous is leaving your number: you hang up, get on with your day, and they call you when it arrives. These three styles — blocking, non-blocking, and asynchronous — are exactly the ways a program can wait for I/O, and asynchronous I/O is the leave-your-number style.
Concretely, contrast them. A blocking read(fd, buf, n) does not return until the data is in buf; the calling thread sleeps meanwhile, which is simple but stalls that thread. A non-blocking read returns immediately with either the data or a would-block status, so the program must keep polling or asking again. Asynchronous I/O goes further: the program submits the request and the call returns at once, the OS performs the transfer in the background, and completion is reported later — via a callback, a signal, or by the program asking an event-notification interface (select, poll, epoll, io_uring) which of its many outstanding operations have finished. The key idea is overlap: useful computation, or the management of thousands of other connections, proceeds while the I/O is in flight.
Why it matters: asynchronous I/O is how a single thread can juggle huge numbers of slow operations at once — the foundation of high-performance servers that handle tens of thousands of network connections without one thread per connection. It is also the most complex of the three styles, because the program must track outstanding requests and handle completions out of order, which is harder to reason about and to get right. The honest framing: blocking is simplest and often perfectly fine; reach for asynchronous I/O when you genuinely need one thread to wait on many things at once.
A web server tells the OS 'start reading from these 5000 sockets' and keeps running. It does not block on any one; when data arrives on socket 837, the OS notifies it (via epoll), and it handles just that one — serving thousands of clients on a single thread.
Submit, carry on, and be told later which operations completed — no thread sits idle.
Asynchronous is not the same as non-blocking: non-blocking still has you ask again and again, while asynchronous has the OS notify you on completion. And async does not make a single transfer faster — it lets you overlap many transfers with other work.