Imagine ordering a burger, then doing something else while it cooks. A coroutine works much like that: it is a function that can pause partway through, let another task run, and later resume where it stopped. section 12.4 section 12.6
This is cooperative multitasking. An operating system can preempt a thread, but a running coroutine must hand over control itself. Pausing while a network request waits lets another task use the same thread. section 12.4
Taking turns with Fibonacci
A Python Fibonacci generator pauses between values. An event loop resumes it, puts it back in a task queue if unfinished, and eventually observes its completion. The example makes the handoff visible one loop cycle at a time. section 12.4
# Chapter 12/coroutine.py
from collections import deque
import typing as T
Coroutine = T.Generator[None, None, int]
class EventLoop:
def __init__(self) -> None:
self.tasks: T.Deque[Coroutine] = deque() #1
def add_coroutine(self, task: Coroutine) -> None:
self.tasks.append(task) #2
def run_coroutine(self, task: Coroutine) -> None:
try:
task.send(None) #3
self.add_coroutine(task)
except StopIteration: #4
print("Task completed")
def run_forever(self) -> None: #5
while self.tasks: #5
print("Event loop cycle.") #5
self.run_coroutine(self.tasks.popleft()) #5
def fibonacci(n: int) -> Coroutine:
a, b = 0, 1
for i in range(n):
a, b = b, a + b
print(f"Fibonacci({i}): {a}")
yield #6
return a #7
if __name__ == "__main__":
event_loop = EventLoop()
event_loop.add_coroutine(fibonacci(5))
event_loop.run_forever()
The small Python event loop shows how a deque holds tasks, send(None) resumes one coroutine, and StopIteration marks completion. section 12.4
For an operation whose result is not ready yet, a coroutine can yield a Future—a placeholder for that result. In a nonblocking socket server, the event loop tracks tasks waiting for I/O and resumes them when the relevant socket event occurs. section 12.6 section 12.8
The trade-off is control:
- Coroutines can share one thread with less task-management overhead than many system threads, particularly for numerous I/O tasks. section 12.5
- A long-running task can hold up every other coroutine unless it periodically yields; the event loop cannot interrupt it. section 12.5 section 12.8







