Python Coroutines Explained – asyncio, await, gather Examples
Introduction – Why Use Coroutines in Python?
Modern Python applications—like web servers, real-time APIs, and event-driven systems—demand non-blocking, asynchronous behavior. Enter coroutines.
Coroutines in Python allow you to:
- Pause and resume execution
- Run concurrent tasks without threads
- Build highly responsive, non-blocking applications
Coroutines are the foundation of Python’s async/await syntax introduced in Python 3.5+.
In this guide, you’ll learn:
- What coroutines are and how they differ from generators
- How to define and run async coroutines
- Real-world examples using
asyncio - Best practices and when to use coroutines over threads
What Is a Coroutine?
A coroutine is a special function declared with async def that can pause execution with await and resume later.
import asyncio
async def greet():
print("Hello")
await asyncio.sleep(1)
print("World")
You can only await inside an async def function.
How to Run a Coroutine
async def main():
await greet()
asyncio.run(main())
Output:
Hello
(wait 1 second)
World
asyncio.run() is the recommended way to start the event loop in Python 3.7+.
Coroutines vs Generators
| Feature | Generators | Coroutines (async) |
|---|---|---|
| Keyword | def, yield | async def, await |
| Use Case | Lazy iteration | Asynchronous programming |
| Pause With | yield | await |
| Scheduler | Manual or loops | asyncio event loop |
️ Example – Simulate Concurrent Tasks
import asyncio
async def task(name, delay):
print(f"{name} started")
await asyncio.sleep(delay)
print(f"{name} completed")
async def main():
await asyncio.gather(
task("Task 1", 2),
task("Task 2", 1)
)
asyncio.run(main())
Output:
Task 1 started
Task 2 started
Task 2 completed
Task 1 completed
Coroutines run concurrently but not in parallel (no threads involved).
Awaitable Objects
You can await on:
- Coroutines (
async def) asyncio.sleep,asyncio.open_connection, etc.- Objects implementing
__await__()
Create Custom Awaitables
class Wait:
def __await__(self):
yield from asyncio.sleep(1).__await__()
async def use_wait():
print("Waiting...")
await Wait()
print("Done")
asyncio.run(use_wait())
You can define your own awaitable classes for advanced control.
Real-World Use Case – Async HTTP Requests
import aiohttp
import asyncio
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
html = await fetch("https://example.com")
print(html[:100])
asyncio.run(main())
No threads, no blocking—ideal for I/O-heavy workloads like web scrapers or APIs.
Coroutines Are Not Threads
| Feature | Coroutine | Thread |
|---|---|---|
| Switch Time | Explicit (await) | Preemptive (by OS) |
| Memory | Lightweight | Heavier |
| Parallelism | No (single-threaded) | Yes (true parallel threads) |
| Best For | I/O-bound tasks | CPU-bound or parallel tasks |
Best Practices
| Do This | Avoid This |
|---|---|
Use asyncio.run() to start your app | Nesting asyncio.run() inside coroutines |
Use await for non-blocking I/O | Calling blocking code inside coroutines |
Use asyncio.gather() for concurrent tasks | Awaiting tasks one-by-one unnecessarily |
Use libraries like aiohttp, aiomysql | Mixing async and sync I/O |
Summary – Recap & Next Steps
Python coroutines enable non-blocking, asynchronous programming using modern syntax (async/await). They’re powerful tools for building scalable, I/O-bound applications.
Key Takeaways:
-
async defdefines a coroutine - Use
awaitto pause execution - Use
asyncio.run()to launch coroutines - Coroutines are lightweight and ideal for I/O-bound tasks
- Don’t use coroutines for CPU-bound tasks—use multiprocessing instead
Real-World Relevance:
Used in async web frameworks (FastAPI, Sanic), chatbots, IoT systems, web scrapers, and real-time APIs.
FAQ – Python Coroutines
What’s the difference between a coroutine and a regular function?
Coroutines are declared with async def and use await to pause and resume.
Can coroutines run in parallel?
No. Coroutines run concurrently on the same thread. Use threads or processes for true parallelism.
How do I call a coroutine?
Use await coroutine() inside another coroutine, or asyncio.run() at the top level.
Can I use await outside of a function?
No. await must be used inside an async def function.
Are coroutines faster than threads?
For I/O-bound tasks, yes. They use less memory and have lower overhead.
Share Now :
