Een Async contextmanager is een object dat de asynchrone methoden __aenter__ en __aexit__ definieert en wordt gebruikt in de async with-constructie. Zo'n manager is nodig voor het correct openen/sluiten van bronnen in asynchrone functies: verbindingen met databases, bestanden, sessies, enzovoort.
Voorbeeld van implementatie:
class AsyncDBConnection: async def __aenter__(self): self.conn = await async_db_connect() return self.conn async def __aexit__(self, exc_type, exc, tb): await self.conn.close() async def main(): async with AsyncDBConnection() as conn: await conn.query('SELECT 1')
Een asynchrone context voorkomt dat de event loop wordt geblokkeerd door technische vertragingen, waardoor de prestaties van concurrente programma's worden verbeterd.
Kan je een gewone with gebruiken binnen async def?
Antwoord: Ja, maar als de bewerkingen binnen de contextmanager awaitable zijn (eisen await), dan is async with nodig. Gewone with ondersteunt geen asynchrone enter/exit, het zal de event loop blokkeren of een fout veroorzaken als await binnen enter/exit wordt aangeroepen.
Voorbeeld:
async def foo(): with open('file.txt') as f: # dit is ok, lezen is synchroon data = f.read() # Maar je kunt geen await gebruiken binnen een gewone context, alleen binnen async with
Verhaal
Project: Webservice met een asynchrone API.
Probleem: De verbinding manager gebruikte een gewone with, maar binnenin werd await aangeroepen. Dit leidde tot een fout RuntimeError("Cannot use 'await' outside async function") en blokkeerde de event loop in productie.
Verhaal
Project: Chat op Websockets.
Probleem: Tijdens het werken met verbindingen werden websocket-bronnen niet gesloten (gebruikten een gewone synchrone manager), wat leidde tot geheugentekorten en vastgelopen verbindingen.
Verhaal
Project: Multithreaded asynchrone taakqueue.
Probleem: De takenmanager implementeerde
__aexit__onjuist, vergat een awaitable terug te geven. Hierdoor werden taken niet gegarandeerd beëindigd en bleven sommige taken "hangen" in het systeem.