asyncio Basics
You already know this — almost
Section titled “You already know this — almost”Python’s async/await syntax was deliberately designed to feel familiar to developers coming from JavaScript. The mental model is nearly identical:
- An
async deffunction returns a coroutine (not a value directly) — just like anasync functionreturns aPromise. awaitsuspends the current coroutine and yields control to the event loop — exactly likeawaitin JS.- The event loop runs coroutines cooperatively — one at a time, switching when something awaits.
The differences are small but important — let’s call them out explicitly.
Defining async functions
Section titled “Defining async functions”// TypeScriptasync function greet(name: string): Promise<string> { // async function implicitly returns Promise<string> return `Hello, ${name}!`;}
const result = await greet("Alice");console.log(result); // Hello, Alice!# Pythonimport asyncio
async def greet(name: str) -> str: # async def returns a coroutine object return f"Hello, {name}!"
# Must await it to get the valueresult = await greet("Alice")print(result) # Hello, Alice!Key similarity: annotating the return type of an async def as str means “this coroutine will produce a str when awaited” — the same as Promise<string> in TypeScript.
await asyncio.sleep vs setTimeout/Promise delay
Section titled “await asyncio.sleep vs setTimeout/Promise delay”The most common “wait without blocking” pattern:
// TypeScript — delay without blocking the threadfunction delay(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms));}
async function run(): Promise<void> { console.log("start"); await delay(1000); console.log("1 second later");}# Python — asyncio.sleep is the direct equivalentimport asyncio
async def run() -> None: print("start") await asyncio.sleep(1) # suspends, yields to event loop print("1 second later")await asyncio.sleep(0) (zero seconds) is the Python equivalent of await Promise.resolve() — it yields to the event loop for one cycle without actually waiting.
Running the event loop
Section titled “Running the event loop”// TypeScript/Node.js// The runtime starts the event loop for you automatically.// Top-level await works in ESM modules:const result = await fetchSomething();
// In CommonJS you wrap in an IIFE:(async () => { const result = await fetchSomething();})();# Python — you must start the event loop explicitlyimport asyncio
async def main() -> None: print("running inside the event loop")
# asyncio.run() creates a new event loop, runs main(), then closes it.# This is the standard entry point for asyncio programs.asyncio.run(main())
# In a Jupyter notebook or interactive session, a loop is already# running — use top-level await directly instead of asyncio.run().A complete async example
Section titled “A complete async example”// TypeScriptasync function fetchData(url: string): Promise<string> { const response = await fetch(url); return response.text();}
async function main(): Promise<void> { console.log("fetching..."); // simulate with a delay await new Promise((r) => setTimeout(r, 500)); console.log("done");}
await main();# Pythonimport asyncio
async def fetch_data(label: str) -> str: # Simulate a network call await asyncio.sleep(0.01) return f"data from {label}"
async def main() -> None: print("fetching...") result = await fetch_data("api.example.com") print(result) print("done")Try it
Section titled “Try it”import asyncio
async def say_after(delay: float, message: str) -> str: await asyncio.sleep(delay) return message
# Top-level await works here (Pyodide runs code as a coroutine)result = await say_after(0.05, "Hello from asyncio!")print(result)
# Chain awaitsa = await say_after(0.01, "first")b = await say_after(0.01, "second")print(a, "->", b)print("Event loop managed both without blocking")Loading Python runtime (first run only)…