Featured image of post Asyncio Best Practices and Common Pitfalls

Asyncio Best Practices and Common Pitfalls

Learn the essential best practices and avoid common mistakes when working with Python's asyncio library

Writing Better Asyncio Code: Dos and Don’ts

Python’s asyncio library is powerful but comes with its own set of conventions and potential pitfalls. Let’s explore the best practices to follow and common mistakes to avoid.

Best Practices

1. Use asyncio.run() as Your Main Entry Point

Always use asyncio.run() to start your asyncio programs:

1
2
3
4
5
6
7
# Good practice
async def main():
    # Your async code here
    pass

if __name__ == "__main__":
    asyncio.run(main())

This function properly sets up and tears down the event loop, and handles cleanup when exceptions occur.

2. Prefer Async Context Managers

When available, use async context managers (async with) for proper resource management:

1
2
3
4
# Good practice
async with aiohttp.ClientSession() as session:
    async with session.get(url) as response:
        data = await response.text()

3. Always Await Coroutines

One of the most common mistakes is forgetting to await coroutines. Always await them:

1
2
3
4
5
6
7
8
# Bad - coroutine is created but never executed
fetch_data(url)

# Good - coroutine is awaited and executed
await fetch_data(url)

# Also good - coroutine is executed via asyncio.run()
asyncio.run(fetch_data(url))

4. Handle Cancellation Gracefully

Always catch CancelledError and clean up resources in tasks that might be cancelled:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
async def cancellable_task():
    try:
        # Your task code
        while True:
            await asyncio.sleep(1)
            # Do work...
    except asyncio.CancelledError:
        # Clean up resources here
        print("Task was cancelled, cleaning up...")
        raise  # Re-raise so caller knows task was cancelle