Your Sunday, upgraded. Free live AI class · 5 PM ISTReserve your seat
PROGRAMMING LANGUAGES

Use Python asyncio for Waiting, Not for Heavy Computation

asyncio can make a program that calls many APIs dramatically faster, and make a CPU-heavy program no faster at all. Learn what it actually speeds up and how to use it safely.

On this page

The core idea

asyncio runs many coroutines on a single thread. When one coroutine awaits something slow, such as a network response or a database query, the event loop switches to another coroutine that is ready to run. This overlaps waiting time, which is why fetching a hundred URLs concurrently can take roughly as long as the slowest few rather than the sum of all of them. It does not run Python code in parallel. A coroutine that performs a long calculation without awaiting blocks every other coroutine until it finishes.

A practical approach

Step 1

Confirm the work is I/O-bound: most time is spent waiting on networks, disks or databases, not computing.

Step 2

Use async-capable libraries throughout, such as an async HTTP client and an async database driver. Calling a blocking library inside a coroutine blocks the whole loop.

Step 3

Start concurrent work with asyncio.gather or, on Python 3.11 and later, asyncio.TaskGroup, which also cancels sibling tasks if one fails.

Step 4

Limit concurrency with asyncio.Semaphore so you do not open hundreds of connections at once or exceed an API's rate limit.

Step 5

Move unavoidable blocking or CPU-heavy calls off the loop with asyncio.to_thread, or into a process pool for real CPU work.

Worked example

A script checks 500 course links by requesting each URL in sequence and takes about eight minutes. Rewriting it with an async HTTP client, a semaphore limiting concurrency to 20 and a TaskGroup brings the run down to well under a minute. A later version that also parses large PDFs inside the same coroutines slows down again, because parsing is CPU-bound; moving parsing to a process pool restores the speed.

A mistake to avoid

Do not call time.sleep() or a synchronous HTTP library inside async code. They block the event loop and silently remove the benefit of async. Use await asyncio.sleep() and async clients instead.

Try it yourself

Write a script that fetches ten pages sequentially, then rewrite it with asyncio and a semaphore of three. Time both versions, then add a deliberately CPU-heavy function inside one coroutine and observe its effect on the others.

Your implementation checklist

  • I use asyncio only for I/O-bound work
  • Every library called in coroutines is async-capable
  • Concurrency is limited with a semaphore
  • Task groups or gather manage concurrent tasks and failures
  • Blocking and CPU-heavy work runs outside the event loop

Share or cite this guide

Innovate with Ajay Editorial (2026-10-07). Use Python asyncio for Waiting, Not for Heavy Computation. https://innovatewithajay.com/blogs/use-asyncio-for-waiting-not-computation

Editorial approach, sources and corrections

YOUR NEXT CHAPTER

You have the curiosity.
Let’s turn it into capability.

Start with a practical workshop or bring a specific question to Ajay.

Chat with Ajay