Python 3.14 ยท
โ verified by execution on 2026-07-30
Asyncio is a library to write concurrent code using the async/await syntax. It is the foundation of multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.
Coroutines and the Event Loop
A coroutine is a function you define with async def. Calling it does not run it โ it hands you an object that only makes progress when something awaits it. That is the whole trick behind asyncio: your code decides exactly where it is willing to pause.
asyncio.run() is the doorway from ordinary synchronous Python into that world. It starts an event loop, runs your top-level coroutine to completion, and shuts the loop down again. You call it once, at the entry point โ never inside another coroutine.
Awaiting a coroutine directly runs it to completion before moving on. Wrapping it in a Task schedules it instead, so the loop can start it and immediately go do something else. Tasks are how you get concurrency rather than just asynchronous-looking sequential code.
The scheduling is cooperative, and that word is doing a lot of work. One task runs at a time; the loop can only switch when a task hits an await and voluntarily yields. A task that never awaits โ a long calculation, say โ blocks everything else, because nothing can interrupt it.
asyncio.TaskGroup (Python 3.11+) is the modern way to run several tasks together. It waits for all of them, and if any one fails it cancels the rest and propagates the error โ no silently-abandoned tasks, which was easy to end up with using bare gather().
Using a TaskGroup context manager
python
import asyncioasync def worker(name): print(f"Working on {name}")async def main(): async with asyncio.TaskGroup() as tg: tg.create_task(worker("A")) tg.create_task(worker("B")) print("All tasks completed")asyncio.run(main())
Output
Working on A
Working on B
All tasks completed
Your output
Concurrent execution visualization
You can spawn hundreds of tasks instantly. The total time depends only on the longest individual delay, because all waiting happens concurrently!
python ยท visualize
import asyncioasync def process_item(item_id): await asyncio.sleep(0.01 * (5 - item_id)) print(f"Processed item {item_id}")async def main(): tasks = 3 print(f"Starting {tasks} tasks") async with asyncio.TaskGroup() as tg: for i in range(1, tasks + 1): tg.create_task(process_item(i)) print("All done")asyncio.run(main())
By using cooperative multitasking on a single thread. โ asyncio is single-threaded and uses cooperative multitasking, meaning it achieves concurrency but not true parallelism.
How should you pause a coroutine to wait for a specific duration?
Reveal answer
Use await asyncio.sleep() โ Using blocking functions like time.sleep() will block the entire event loop, stopping all other tasks. You must use await asyncio.sleep().
What happens when you call a function defined with `async def` without using `await`?
Reveal answer
It returns a coroutine object but does not execute its body. โ Calling a coroutine function only returns a coroutine object. It doesn't run until you await it or schedule it in an event loop.
Challenges
Challenge 1 +100 XP
Write an async function countdown(start) that prints the starting number, awaits for 0.01 seconds, and then prints 'Go!'. Then create a main coroutine to run it with a start value of 3.
python
Test 1 โ expects "3\nGo!\n"
Need a hint? (โ25% XP)
Use await asyncio.sleep(0.01) to pause the coroutine without blocking.
Use asyncio.TaskGroup to run three tasks concurrently. Create an async function ping(name) that prints f'Ping {name}'. In main(), use a TaskGroup to spawn three ping tasks for 'A', 'B', and 'C'.
python
Test 1 โ expects "Ping A\nPing B\nPing C\n"
Need a hint? (โ25% XP)
Use the async with asyncio.TaskGroup() as tg: pattern and tg.create_task().
Show solution (0 XP)
import asyncioasync def ping(name): print(f'Ping {name}')async def main(): async with asyncio.TaskGroup() as tg: tg.create_task(ping('A')) tg.create_task(ping('B')) tg.create_task(ping('C'))asyncio.run(main())