Python has had async for 10 years – why isn't it more popular?
Perceived complexity and ergonomics
- Many find
asyncio“awful”: hard to reason about, easy to deadlock, and very unforgiving if any code accidentally blocks (e.g.,time.sleep, heavy CPU work). - Async “infects” code: once you introduce
async def,awaittends to propagate through the call stack, effectively creating two incompatible APIs (“function coloring”). - Python’s model involves many moving parts (coroutines, awaitables, futures, tasks, event loops, async iterators), which users compare unfavorably to simpler mental models in other ecosystems.
- Debugging is cited as painful: lost stack traces, confusing cancellation via exceptions, KeyboardInterrupt weirdness, context leaks across requests, and libraries swallowing cancellation exceptions.
- Documentation is criticized as written for people who already understand coroutines/futures, with poor guidance on how to structure real applications and avoid footguns.
Limited practical payoff
- Async only meaningfully helps IO‑bound concurrency; many Python workloads (data science, ML inference, batch jobs, CLIs, CPU‑bound work) don’t benefit.
- For web backends that are mostly DB + templating, simple process/thread pools (gunicorn, WSGI, Celery,
threading/multiprocessing) are “good enough” and much easier to reason about. - A single CPU‑heavy task in an event loop can stall all other requests, undermining the selling point of evented servers.
- With multi‑core machines and process‑based scaling commonplace, many see little reason to pay the async complexity tax.
Ecosystem and historical baggage
- Before
asyncio, Python had Twisted, Tornado, gevent, threads, multiprocessing, Celery, etc. By the timeasync/awaitlanded, high‑concurrency users already had solutions. - WSGI, blocking stdlib APIs, and popular C‑extension libraries are deeply entrenched; retrofitting them with async variants means dual APIs that are costly to build and maintain.
- Key async pieces (DB drivers, ORMs, HTTP clients, frameworks) arrived slowly and unevenly, reinforcing the split between sync and async codebases.
Alternative concurrency models
- Many prefer green‑thread or virtual‑thread style models (gevent, Go, Erlang/Elixir, Java virtual threads) where code “just blocks” and the runtime handles scheduling, avoiding function coloring.
- Structured concurrency libraries (Trio, anyio) are praised as much nicer than raw
asyncio, especially around cancellation. - Some argue that free‑threading / no‑GIL plus better thread‑pool APIs may ultimately make Python async less important.
Where async Python shines
- Areas repeatedly cited as good fits: high‑connection‑count network servers, websockets, Redis pub/sub, small IO‑heavy microservices (e.g., FastAPI + async DB clients), and glue code over network APIs.