backend / async concurrency / 05_async_python_coroutines.md

Asynchronous Python and Coroutines

4 interview angles 3 min read source

Asynchronous Python and Coroutines

What is Asynchronous Python?

Asynchronous Python allows you to write code that performs non-blocking operations. Instead of waiting for a time-consuming task (like I/O operations) to finish before moving to the next step, asynchronous programming enables other tasks to run concurrently, improving efficiency and responsiveness.

Key Features of Asynchronous Python

  1. Concurrency: Multiple tasks can run in overlapping time periods without being blocked by slow tasks.
  2. Event Loop: Uses an event loop to manage the execution of asynchronous tasks.
  3. Coroutines: The building blocks of asynchronous programming, defined using async def.
  4. Awaitable Objects: Objects like coroutines or tasks that can be awaited using the await keyword.
  5. Asynchronous Libraries: Many libraries (e.g., aiohttp, asyncio) support asynchronous operations.

When to Use Asynchronous Python

  • Handling a large number of I/O-bound tasks (e.g., API calls, file I/O, database queries).
  • Building real-time applications like chat servers or streaming systems.
  • Networking tasks requiring efficient resource usage.

What is a Coroutine?

A coroutine is a special type of function in Python that can pause its execution (using await) and resume later, enabling asynchronous operations.

How to Define a Coroutine

A coroutine is defined using the async def syntax:

import asyncio

async def my_coroutine():
    print("Start")
    await asyncio.sleep(1)  # Simulates a delay or non-blocking task
    print("End")

Running Coroutines

Coroutines need an event loop to run. You can use asyncio.run or schedule them with asyncio.create_task:

# Run a coroutine
asyncio.run(my_coroutine())

Example of Coroutine with Multiple Tasks

async def task_one():
    await asyncio.sleep(2)
    print("Task One Complete")

async def task_two():
    await asyncio.sleep(1)
    print("Task Two Complete")

async def main():
    # Run tasks concurrently
    await asyncio.gather(task_one(), task_two())

asyncio.run(main())
  • Output:
    • Task Two Complete
    • Task One Complete

Advantages of Asynchronous Python

  • Improved Performance: Efficiently handles tasks like network I/O without blocking.
  • Resource Utilization: Better CPU and memory usage by avoiding idle waits.
  • Scalability: Handles thousands of concurrent connections, ideal for web servers.

Differences Between Synchronous and Asynchronous Programming

Aspect Synchronous Asynchronous
Execution Tasks run sequentially. Tasks can run concurrently.
Blocking Blocks execution until task ends. Non-blocking, other tasks run.
Efficiency Less efficient for I/O tasks. Highly efficient for I/O tasks.
Code Style Easier to write and understand. Requires understanding of async.

Summary

  • Asynchronous Python enables non-blocking operations, ideal for I/O-bound tasks.
  • Coroutines are the core of async programming, defined using async def.
  • Use libraries like asyncio to build efficient and scalable applications.
  • Understanding asynchronous programming is essential for building modern Python applications such as web servers, real-time apps, and networking tools.

Interview angle

  • “What does calling an async def function return?” - a coroutine object, not a result. Nothing executes until it’s awaited or scheduled on the loop. Forgetting to await is the most common async bug, and it surfaces as a “coroutine was never awaited” warning rather than an error.
  • await versus create_task?” - await runs it now and waits; create_task schedules it to run concurrently and returns a handle. Awaiting a list of coroutines sequentially in a loop is the classic accidental-serialisation bug.
  • “What makes something awaitable?” - it implements __await__. Coroutines, Tasks and Futures all qualify; you almost never implement it yourself.
  • “Why does one blocking call ruin everything?” - it’s a single-threaded event loop, so a synchronous call holds the only thread and every other coroutine stalls. The signature is p99 latency rising across all endpoints at once. See 14_run_in_executor_to_thread.md.