Python by Contrast
Async by contrast
Lesson 4 of 5
What you'll learn
- Start an event loop explicitly with asyncio.run
- Map async def, await and gather onto async, await and Promise.all
- Understand why a blocking sync call freezes the whole loop
In JS the event loop is the runtime — every script already lives inside it. Python programs are synchronous by default; the asyncio event loop exists only once you start it, and the idiomatic entry point is a single asyncio.run(main()) at the bottom of the file.
import asyncio
async def fetch(name: str, delay: float) -> str:
await asyncio.sleep(delay) # non-blocking: yields to the loop
return f"{name} done"
async def main() -> None:
results = await asyncio.gather(
fetch("a", 0.3), fetch("b", 0.2), fetch("c", 0.1)
)
print(results) # ['a done', 'b done', 'c done'] — order preserved
asyncio.run(main()) # starts the loop, runs main, closes the loop
The TS shape is nearly identical, minus the explicit startup — and asyncio.gather is Promise.all, down to preserving input order:
async function fetchOne(name: string, ms: number): Promise<string> {
await new Promise((r) => setTimeout(r, ms));
return `${name} done`;
}
const results = await Promise.all([fetchOne("a", 300), fetchOne("b", 200)]);
Coroutines are lazy
Calling a JS async function starts it immediately — promises are eager. Calling fetch("a", 0.3) in Python starts nothing: it returns a coroutine object that only runs once awaited or scheduled (asyncio.create_task is the "start now" move, like calling an async function in JS and awaiting later).
One blocking call freezes everything
The loop is cooperative and single-threaded, like JS. A synchronous call inside a coroutine — time.sleep(1), a requests.get — doesn't just slow that task, it blocks the entire loop: nothing else runs until it returns. Use asyncio.sleep, an async client like httpx, or push sync work off the loop with asyncio.to_thread.
await is not concurrency
Awaiting coroutines one at a time serializes them, exactly like sequential awaits in JS. gather is the concurrency move for I/O; CPU-bound work still wants threads or processes, not the event loop.
The challenge is a JS model of gather: the same delayed tasks run one at a time, then together — watch the timings.
Run it. This models asyncio.gather with Promise.all: sequential awaits cost the sum of the delays, gather costs only the longest one.
What does calling an async def function (without awaiting it) do in Python?
Next: the standard library that makes Python the default language of ops scripts and glue code.
Saved on this device. Sign in to sync your progress everywhere.