An event loop repeatedly selects runnable tasks, executes them until they finish or pause, and resumes paused tasks when their I/O becomes ready. In asyncio, this enables concurrent I/O-bound work within one Python thread instead of blocking while each operation waits. section 1.7 section 1.8
A typical asyncio iteration follows this sequence:
- Runs ready tasks one at a time until a coroutine reaches an await for I/O or another future result. section 1.7
- Registers non-blocking sockets with the operating system’s event notification system while the suspended task waits. section 1.6.1 section 3.4
- Waits for socket activity or a scheduled timeout, then turns completed events into tasks ready to run. section 3.4
- Resumes awakened coroutines and repeats indefinitely, allowing other tasks to progress while one task waits. section 1.7
From sockets to an echo server
A selector-based loop can support multiple clients by reading or writing only when sockets are ready. asyncio adds coroutines and tasks above this mechanism, so an echo server can accept connections and process client data concurrently without directly managing selector details. The same model does not make CPU-bound coroutines parallel: such work can block the loop and may need a process pool. section 3.5 section 2.7.1
Figure 1.8 Making a non-blocking I/O request returns immediately and tells the O/S to watch sockets for data. This allows execute_other_code() to run right away instead of waiting for the I/O requests to finish. Later, we can be alerted when I/O is complete and process the response.
Trace how non-blocking I/O returns immediately, lets other code run, and later wakes the waiting task when the operating system reports completion. section 1.6.1










