Skip to content

asyncio Basics

Python’s async/await syntax was deliberately designed to feel familiar to developers coming from JavaScript. The mental model is nearly identical:

  • An async def function returns a coroutine (not a value directly) — just like an async function returns a Promise.
  • await suspends the current coroutine and yields control to the event loop — exactly like await in 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.

TypeScript
// TypeScript
async 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!
Python
# Python
import asyncio
async def greet(name: str) -> str:
# async def returns a coroutine object
return f"Hello, {name}!"
# Must await it to get the value
result = 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
// TypeScript — delay without blocking the thread
function 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
# Python — asyncio.sleep is the direct equivalent
import 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.

TypeScript
// 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
# Python — you must start the event loop explicitly
import 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().
TypeScript
// TypeScript
async 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();
Python
# Python
import 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")
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 awaits
a = await say_after(0.01, "first")
b = await say_after(0.01, "second")
print(a, "->", b)
print("Event loop managed both without blocking")
What does an `async def` function return when called without `await`?
What is the Python equivalent of JavaScript's `await new Promise(r => setTimeout(r, 1000))`?
When should you use `asyncio.run(main())` vs just `await main()`?
What does `await asyncio.sleep(0)` do?