π 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 def
defines a coroutine - β
Use
await
to 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 :