Python - Asyncio

Overview

Estimated time: 30–45 minutes

asyncio enables concurrent I/O via coroutines and an event loop. Learn how to structure async code safely and avoid blocking the loop.

Learning Objectives

  • Write coroutines with async def and await.
  • Run tasks concurrently and handle cancellations/timeouts.
  • Recognize when to use threads/processes vs asyncio.

Prerequisites

  • Functions, exceptions; basic understanding of I/O-bound vs CPU-bound work

Examples

import asyncio

async def work(name, delay):
    await asyncio.sleep(delay)
    return f"{name} done"

async def main():
    t1 = asyncio.create_task(work("A", 1))
    t2 = asyncio.create_task(work("B", 1.5))
    results = await asyncio.gather(t1, t2)
    print(results)

asyncio.run(main())

Expected Output:

['A done', 'B done']

Common Pitfalls

  • Blocking the event loop with CPU-bound work or synchronous I/O—offload via asyncio.to_thread or run_in_executor.
  • Forgetting to handle cancellations and timeouts; use asyncio.wait_for and try/finally for cleanup.
  • Mixing sync and async APIs; prefer libraries with native async support.

Best Practices

  • Use asyncio.create_task for fire-and-forget, track tasks to avoid leaks.
  • Apply timeouts and cancellation handling; ensure idempotent cleanup.
  • Keep coroutines small and composable; avoid deep nesting.

Checks for Understanding

  1. How do you run two coroutines concurrently and get both results?
  2. How do you offload CPU-bound work in an async app?
Show answers
  1. Create tasks and await asyncio.gather(...).
  2. Use asyncio.to_thread (3.9+) or loop.run_in_executor with a ThreadPool/ProcessPool.

Exercises

  1. Implement concurrent HTTP GETs using aiohttp (or httpx in async mode) with timeouts.
  2. Demonstrate cancellation of a long-running task and proper resource cleanup.