You have written async def and await . You know the surface. At some point something deadlocked, or performed worse than expected, or behaved in a way that did not fit your mental model. This article is about building the mental model that makes those situations predictable rather than surprising. Coroutines Are Not Threads and Not Callbacks The two common async models before asyncio were threads and callbacks. Threads give you apparent concurrency with shared state and all the locking that entails. Callbacks give you non-blocking IO with inverted control flow that becomes unreadable at scale. Coroutines are a third model. A coroutine is a function that can suspend its execution at specific points and resume from exactly that point later. The suspension is cooperative: the coroutine decides when to yield control, not a scheduler. The interpreter does not preempt it. This is the first thing to internalize. Async Python is not parallel.…