Python
async/await
asynchronous programming
beginner tutorial
code example

Simplest async/await example possible in Python

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

The simplest useful async and await example in Python should teach one idea clearly: an async def function creates a coroutine, and await pauses that coroutine so the event loop can run other work. Once that mental model is clear, the rest of asyncio becomes much easier to understand.

The Smallest Example

Start with one coroutine and run it with asyncio.run.

python
1import asyncio
2
3async def hello():
4    print("start")
5    await asyncio.sleep(1)
6    print("end")
7
8asyncio.run(hello())

This is a complete runnable example. asyncio.sleep(1) is important because it is an awaitable operation that gives control back to the event loop instead of blocking the thread.

What async def Actually Creates

Calling hello() does not immediately run the function body to completion the way a normal function call would. It creates a coroutine object that must be awaited or run by the event loop.

python
1import asyncio
2
3async def hello():
4    return "done"
5
6async def main():
7    coro = hello()
8    result = await coro
9    print(result)
10
11asyncio.run(main())

That is why await can only be used inside another async function.

Returning Values

Coroutines can return values just like regular functions.

python
1import asyncio
2
3async def add(a, b):
4    await asyncio.sleep(0)
5    return a + b
6
7async def main():
8    result = await add(2, 3)
9    print(result)
10
11asyncio.run(main())

This example is still simple, but it shows that async functions are not limited to printing or side effects.

Running Multiple Coroutines Together

To see why await matters, run several coroutines concurrently with asyncio.gather.

python
1import asyncio
2
3async def job(name, delay):
4    await asyncio.sleep(delay)
5    return f"{name} done"
6
7async def main():
8    results = await asyncio.gather(
9        job("A", 0.3),
10        job("B", 0.1),
11        job("C", 0.2),
12    )
13    print(results)
14
15asyncio.run(main())

These coroutines overlap in time cooperatively because each one awaits a non-blocking operation.

What Not to Do

Beginners often expect async alone to make code asynchronous. It does not. If you call blocking code like time.sleep, you block the whole thread and therefore block the event loop too.

python
1import asyncio
2import time
3
4async def bad_example():
5    print("before blocking")
6    time.sleep(1)
7    print("after blocking")
8
9asyncio.run(bad_example())

This runs, but it defeats the purpose of async programming. Use await asyncio.sleep(...) for a simple non-blocking pause.

A Good Beginner Mental Model

Think of the event loop as a scheduler for coroutines. When a coroutine reaches await, it gives the loop a chance to run something else. That is why async code works best for I/O-bound work such as:

  • network requests
  • database calls through async drivers
  • waiting on subprocesses
  • timers and sockets

It is not a magic speed-up for CPU-heavy code.

When You Need Tasks

asyncio.gather is enough for many examples, but you will often see asyncio.create_task in larger programs. A task is just the event loop's managed wrapper around a coroutine.

python
1import asyncio
2
3async def worker():
4    await asyncio.sleep(0.1)
5    print("worker done")
6
7async def main():
8    task = asyncio.create_task(worker())
9    await task
10
11asyncio.run(main())

You do not need tasks for the smallest examples, but it helps to know where the abstraction goes next.

Common Pitfalls

The biggest pitfall is forgetting to await a coroutine. If you create a coroutine object and never await it, the intended work never really happens.

Another issue is putting blocking calls such as time.sleep() inside async code. That blocks the event loop instead of cooperating with it.

Developers also try to use await at top level in ordinary Python scripts. In normal scripts, wrap async code in asyncio.run(...).

Finally, do not assume async means parallel CPU execution. asyncio is mainly about cooperative concurrency for waiting-heavy tasks.

Summary

  • Define coroutines with async def.
  • Use await inside async functions to pause cooperatively on awaitable work.
  • Start a top-level async program with asyncio.run(...).
  • Use asyncio.sleep() in examples instead of blocking calls such as time.sleep().
  • Reach for asyncio.gather() or create_task() when you want several coroutines to make progress together.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.