DEV Community

M.T.Ramkrushna
M.T.Ramkrushna

Posted on

Async/Await in Python Explained Using a Food Delivery App

One of the biggest mindset shifts in Python backend development is understanding async/await.

At first, it looks confusing.

But once you connect it to real-life backend problems, it becomes much easier.

Let’s break it down using something relatable:
a food delivery app.


The Problem With Traditional Blocking Code

Imagine 100 users ordering food simultaneously.

Your backend must:

  • verify payment,
  • call restaurant API,
  • assign delivery partner,
  • send notifications.

If every task waits sequentially, your server slows down.


Normal Blocking Example

import time

def prepare_order():
    time.sleep(5)
    return "Food Ready"

print(prepare_order())
Enter fullscreen mode Exit fullscreen mode

The program waits completely for 5 seconds.

This is called blocking execution.


Async Version

import asyncio

async def prepare_order():
    await asyncio.sleep(5)
    return "Food Ready"

async def main():
    result = await prepare_order()
    print(result)

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

Now Python can work on other tasks while waiting.


Real-Life Analogy

Think of a restaurant waiter.

Bad system:

  • waiter stands near kitchen doing nothing until food is ready.

Async system:

  • waiter takes other orders while kitchen cooks.

That’s exactly what async programming does.


Where Async Actually Helps

Async shines when dealing with:

  • APIs,
  • databases,
  • notifications,
  • sockets,
  • queues,
  • AI inference calls,
  • web scraping.

Not CPU-heavy work.


Combining Async With Classes

import asyncio

class NotificationService:

    async def send_sms(self, user: str):
        await asyncio.sleep(2)
        print(f"SMS sent to {user}")

async def main():
    service = NotificationService()

    await service.send_sms("Ali")

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

This pattern is extremely common in FastAPI systems.


Add Type Hints for Cleaner APIs

class NotificationService:

    async def send_sms(self, user: str) -> None:
        print(f"SMS sent to {user}")
Enter fullscreen mode Exit fullscreen mode

Type hints become very useful in async-heavy codebases.


Using Decorators With Async Functions

Suppose you want request timing.

import time

def timer(func):
    async def wrapper(*args, **kwargs):
        start = time.time()

        result = await func(*args, **kwargs)

        end = time.time()

        print(f"Execution Time: {end - start}")

        return result

    return wrapper
Enter fullscreen mode Exit fullscreen mode

Usage:

class PaymentService:

    @timer
    async def process_payment(self):
        await asyncio.sleep(2)
        print("Payment completed")
Enter fullscreen mode Exit fullscreen mode

Why Modern Backend Frameworks Prefer Async

Frameworks like FastAPI became popular because async allows:

  • better scalability,
  • lower server costs,
  • high concurrent request handling.

This matters massively in:

  • AI apps,
  • chat systems,
  • trading systems,
  • live dashboards,
  • realtime analytics.

Final Thoughts

Async/await feels difficult initially because it changes how we think about execution.

But once you understand:

  • “waiting without blocking”

everything clicks.

And in modern backend engineering, async is no longer optional knowledge.

It’s becoming foundational.

Top comments (0)