ReviseAlgo Logo

Concurrency

Coroutines

Coroutine functions and cooperative multitasking — async def functions that can pause and resume, enabling non-blocking concurrent execution.

Interview: Core async concept — understanding coroutines is essential for modern Python development.

Last Updated: June 12, 2026 9 min read

Coroutines are functions defined with async def that can pause their execution at await points and resume later. Unlike generators (which yield values), coroutines yield control back to the event loop, allowing other coroutines to run while waiting for I/O. They're the building blocks of Python's asyncio framework.

Coroutine Lifecycle

  • Created: Calling an async function returns a coroutine object (not yet running)
  • Running: The event loop executes the coroutine until it hits await
  • Suspended: At await, control returns to the event loop
  • Resumed: When the awaited operation completes, the coroutine continues
  • Completed: The coroutine returns a value or raises an exception

Awaitable Objects

  • Coroutines: async def functions — the most common awaitable
  • Tasks: Coroutines wrapped with asyncio.create_task()
  • Futures: Low-level objects representing eventual results
  • You can await any of these inside an async def function

Coroutine Patterns

  • Chaining: Coroutines can await other coroutines, building complex workflows
  • Nesting: await can appear in expressions — result = await fetch() + await compute()
  • Async context managers: async with for resources that need async setup/teardown
  • Async iterators: async for for streaming data

Interview Insight

Coroutines enable cooperative multitasking — they voluntarily yield control, unlike threads which are preempted. This eliminates many concurrency bugs (no lock needed for shared state modification between await points). Mention that coroutines have near-zero overhead compared to OS threads.

Use Cases

Async pipelines — fetch → transform → store workflows

Streaming data — async iterators for real-time data feeds

Resource management — async context managers for DB connections, file handles

Timeout handling — asyncio.timeout() for bounded async operations

Task cancellation — gracefully stopping long-running operations

Common Mistakes

Forgetting await — coroutine objects are silently created but never executed

Using time.sleep() instead of asyncio.sleep() — blocks the entire event loop

Not handling CancelledError — tasks should clean up resources when cancelled

Creating too many coroutines at once — use asyncio.Semaphore to limit concurrency

Trying to await in __init__ — constructors cannot be async; use factory methods instead