Skip to content

Async Context Managers and Async Generators

Async context managers (async withasync with)

You use async withasync with when acquiring/releasing a resource needs awaitawait.

Common examples:

  • aiohttp.ClientSession()aiohttp.ClientSession()
  • async database connections
  • async locks (asyncio.Lockasyncio.Lock)

Example: sessions and responses

aiohttp_context_manager.py
import asyncio
import aiohttp
 
 
async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.github.com") as resp:
            data = await resp.json()
            print(resp.status)
            print("keys:", list(data.keys())[:5])
 
 
asyncio.run(main())
aiohttp_context_manager.py
import asyncio
import aiohttp
 
 
async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.github.com") as resp:
            data = await resp.json()
            print(resp.status)
            print("keys:", list(data.keys())[:5])
 
 
asyncio.run(main())

Writing your own async context manager

Use @asynccontextmanager@asynccontextmanager for a clean pattern.

custom_async_context_manager.py
import asyncio
from contextlib import asynccontextmanager
 
 
@asynccontextmanager
async def managed_resource(name: str):
    print("acquire", name)
    await asyncio.sleep(0.1)
    try:
        yield {"name": name}
    finally:
        print("release", name)
        await asyncio.sleep(0.1)
 
 
async def main():
    async with managed_resource("db-conn") as res:
        print(res)
 
 
asyncio.run(main())
custom_async_context_manager.py
import asyncio
from contextlib import asynccontextmanager
 
 
@asynccontextmanager
async def managed_resource(name: str):
    print("acquire", name)
    await asyncio.sleep(0.1)
    try:
        yield {"name": name}
    finally:
        print("release", name)
        await asyncio.sleep(0.1)
 
 
async def main():
    async with managed_resource("db-conn") as res:
        print(res)
 
 
asyncio.run(main())

Async generators (async defasync def + yieldyield)

An async generator is a generator that can awaitawait between yields.

It’s perfect for streaming data without loading everything into memory.

Example: async generator + async forasync for

async_generator.py
import asyncio
 
 
async def ticker(n: int, delay: float = 0.2):
    for i in range(n):
        await asyncio.sleep(delay)
        yield i
 
 
async def main():
    async for value in ticker(5):
        print(value)
 
 
asyncio.run(main())
async_generator.py
import asyncio
 
 
async def ticker(n: int, delay: float = 0.2):
    for i in range(n):
        await asyncio.sleep(delay)
        yield i
 
 
async def main():
    async for value in ticker(5):
        print(value)
 
 
asyncio.run(main())

Important gotchas

  • Use async withasync with only with objects that implement __aenter____aenter__ / __aexit____aexit__.
  • Use async forasync for only on async iterables (implement __aiter____aiter__).
  • Don’t forget to close async resources (sessions, connections). async withasync with helps you avoid leaks.

πŸ§ͺ Try It Yourself

Exercise 1 – Your First Coroutine

Exercise 2 – await asyncio.sleep

Exercise 3 – Gather Two Coroutines

If this helped you, consider buying me a coffee β˜•

Buy me a coffee

Was this page helpful?

Let us know how we did