Asynchronous Python (async IO)


Understanding Asynchronous Python (async IO)

Before diving into Agentic frameworks or the OpenAI Agents SDK, there’s one essential concept every builder needs to understand — asynchronous Python, or async IO. It appears in nearly every Agentic framework, and while you can copy the patterns without deep understanding, taking half an hour to really “get it” will pay off again and again. This is adapted from ED Donner’s Udemy course.

Async IO was introduced in Python 3.5 as a lightweight alternative to multithreading or multiprocessing. It lets your code run concurrently without using multiple operating-system threads. Because it’s lightweight, you can run thousands of concurrent operations efficiently — perfect for AI agents that call APIs or wait on network responses.

Why Async IO Matters for Agentic Systems

  • Agent frameworks often make many API calls in parallel (e.g., to OpenAI, Anthropic, or internal tools).
  • Most of the time is spent waiting for network I/O, not computation.
  • Async IO lets other tasks keep running instead of idling while one waits for a response.
  • The result: faster, more scalable multi-agent systems with minimal resource cost.

The Simple Rules

  • Define an asynchronous function with async def.
  • Call it using await instead of a normal function call.
  • Example:
    async def do_processing(): return "done"
    result = await do_processing()

If you remember those two rules — async def to define, await to call — you can already write basic async code.

The Deeper Story

When you write async def, you’re not creating an ordinary function — you’re defining a coroutine.
Calling it doesn’t run the code immediately; it returns a coroutine object that must be awaited.

Behind the scenes, Python’s event loop (from the asyncio library) schedules and manages these coroutines.
The loop runs one coroutine at a time, but whenever one pauses — for example, waiting on a network response — the loop switches to another coroutine that’s ready to continue. This gives the illusion of parallelism while staying single-threaded and extremely efficient.

Running Tasks Together

If you need several coroutines to run concurrently, you can gather them:

results = await asyncio.gather(
    do_task_one(),
    do_task_two(),
    do_task_three()
)

This schedules all three tasks at once; the event loop handles their pauses and resumes. When they all finish, results will contain a list of their return values.

In Short

  • Async IO ≠ multithreading — it’s lighter, safer, and ideal for I/O-bound work.
  • Coroutines are async functions that can be paused and resumed.
  • The event loop orchestrates which coroutine runs next.
  • await schedules a coroutine and waits for its result.

Once you understand these principles, asynchronous Python stops feeling mysterious — and every Agentic framework you touch (CrewAI, LangGraph, AutoGen, OpenAI Agents SDK) will suddenly make more sense.

Created collaboratively by Ed Donner, ChatGPT, and Mike Porter.

Leave a Reply