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, await tends 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 time async/await landed, 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.