The event loop is the core of every asyncio application. Event loops run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses.
Here’s a high-level overview of how an event loop works:
- Initialization: The event loop is created and initialized.
- Registering tasks: Asynchronous tasks, represented by Coroutines or Future objects, are registered with the event loop.
- Waiting for events: The event loop enters a continuous loop and waits for events to occur, such as I/O operations, timers, or signals.
- Dispatching events: When an event is ready, the event loop dispatches the associated task for processing. This can involve executing the
coroutineor resolving theFutureobject. When the event loop dispatches a task, it can take one of two approaches depending on whether the task is acoroutineor aFutureobject:
Coroutines: If the task is acoroutine, the event loop executes thecoroutineuntil it encounters anawaitexpression or reaches the end of thecoroutine. Theawaitexpression typically represents an operation that may block, such as an I/O operation. When thecoroutineencounters anawaitexpression, it suspends its execution and yields control back to the event loop, allowing it to continue with other tasks. Once the awaited operation completes, the event loop resumes the execution of thecoroutinefrom where it left off. This process of suspending and resuming allows the event loop to switch between differentcoroutines, making progress on multiple tasks concurrently.
Futures: If the task is aFutureobject, the event loop resolves theFutureby checking its state. AFuturecan be in one of three states: pending, completed with a result, or completed with an exception. If theFutureis pending, the event loop moves on to other tasks and checks theFutureagain later. If theFutureis completed with a result or an exception, the event loop handles the completion by notifying anycoroutinesor callbacks awaiting theFutureand proceeds to the next ready event.
- Executing tasks: The event loop executes the task until it yields control, such as when it encounters an
awaitexpression or an I/O operation that would block. - Handling results: Once a task yields control, the event loop moves on to the next ready event and dispatches its associated task. The event loop may also handle completed tasks and retrieve their results.
- Exiting the loop: The event loop continues this cycle until there are no more registered tasks or explicit exit conditions are met.
In asyncio, the event loop keeps a queue of tasks instead of messages. Tasks are wrappers around a coroutine. A coroutine can pause execution when it hits an I/O-bound operation and will let the event loop run other tasks that are not waiting for I/O operations to complete
In Python, a basic event loop might look something like this:
from collections import deque
messages = deque()
while True:
if messages:
message =
messages.pop()
process_message(message)