An async function is a function that:
Can pause execution
Lets other code run meanwhile
Then resume from where it stopped
It is defined using:
async def my_function():
...
When you call an async function:
result = my_function()
👉 It does NOT execute immediately
👉 It returns a coroutine object
Execution only starts when you:
await it
or run it inside an event loop (asyncio.run())
def cook():
boil_water()
cut_vegetables()
Chef blocks everything until done.
async def cook():
await boil_water()
cut_vegetables()
When water is boiling (I/O wait), chef serves another customer.
👉 That’s async.
When you write:
async def fetch_data():
return "Data"
And call:
x = fetch_data()
Python:
Creates coroutine object
Does NOT execute body
Waits for event loop
The coroutine object is like:
📦 A paused execution plan
awaitawait?await tells Python:
"Pause here until this async task finishes. Meanwhile, let others run."
async def task():
print("Start")
await asyncio.sleep(2)
print("End")
When execution reaches:
await asyncio.sleep(2)
What happens?
Task pauses
Control goes back to event loop
Event loop runs other tasks
After 2 sec → task resumes
You can ONLY use await:
Inside async functions
On awaitable objects (coroutine, Task, Future)
This is invalid:
def normal():
await something() ❌
asyncio.run()Now big question:
If async function doesn’t run automatically…
Who runs it?
👉 The Event Loop
Event loop is:
The brain that manages and schedules all async tasks.
It:
Picks a task
Runs until it hits await
Switches to another task
Comes back later
asyncio.run() do?asyncio.run(main())
It:
Creates an event loop
Schedules coroutine
Runs until complete
Closes loop
So it’s the entry point of async programs.
Let’s analyze this:
import asyncio
async def greet():
print("Hello")
await asyncio.sleep(2)
print("World")
asyncio.run(greet())
greet() → coroutine object created
asyncio.run() starts event loop
Event loop starts executing greet
Prints "Hello"
Hits await
Pauses task
Event loop waits 2 sec (or runs others)
Resumes task
Prints "World"
Loop closes
Time →
Task A: Start ---- Pause ------ Resume ---- Done
Task B: Start --- Done
Instead of blocking like:
Task A: Start ------------ Done
Task B: Start ----- Done
Async makes concurrency possible.
| Feature | Normal Function | Async Function |
|---|---|---|
| Execution | Immediate | Delayed |
| Returns | Value | Coroutine object |
| Can pause | ❌ | ✅ |
| Needs event loop | ❌ | ✅ |
| Good for CPU tasks | ✅ | ❌ |
| Good for I/O tasks | ❌ | ✅ |
@app.get("/users")
async def get_users():
users = await db.fetch_all()
return users
Here:
Request comes
FastAPI calls async function
DB query happens
While waiting → server handles other users
When DB returns → function resumes
This is why FastAPI handles 10k+ concurrent users efficiently.
data = fetch_data() # WRONG
You get coroutine object instead of result.
time.sleep(5)
This blocks event loop ❌
Correct:
await asyncio.sleep(5)
Async in Python uses:
Generators
Yield mechanics
Event-driven architecture
When you write:
await something()
Internally it's similar to:
yield from something()
Coroutine saves state and returns control to event loop.
That’s why it can resume exactly where it left.
Async ≠ Parallelism
Async = Concurrency
It does NOT use multiple CPU cores automatically.
It works best for:
API calls
Database queries
File operations
Network operations
It is NOT good for:
Heavy ML training
Image processing
CPU heavy loops
For those → use multiprocessing.
You can await:
Coroutine
Task
Future
Example:
task = asyncio.create_task(greet())
await task
Since you're:
Learning FastAPI
Planning backend architecture
Working with PostgreSQL
Using N8N
Building AI systems
Async will control:
DB efficiency
API performance
Scalability
System throughput
If you master this, you become backend architect level.
Think of async system as:
📞 Call center with 1 agent but smart call holding system.
Normal function:
Agent talks to 1 customer until call ends.
Async:
Agent puts customer on hold while checking info, talks to another, then comes back.
Defined with async def
Returns coroutine object
Can pause execution
Pauses execution
Gives control back to event loop
Must be inside async function
Starts event loop
Runs coroutine
Entry point of async program
Manages all async tasks
Handles scheduling
Enables concurrency