Asynchronous Programming in Python

Asynchronous Programming, async, and await in Python

Hello everyone! Today we are exploring Asynchronous Programming in Python—a simple yet powerful concept that helps your code run faster when dealing with time-consuming tasks.


1. What is Asynchronous Programming?

In standard Synchronous programming, Python executes your code line-by-line. If a task takes time—like downloading a image or waiting for a website to respond—Python stops and waits before moving to the next line. This is called blocking.

Asynchronous programming allows Python to work on other tasks while waiting for those slow operations to finish.

The Restaurant Kitchen Analogy

Think of how a restaurant kitchen works:

  • Synchronous (Blocking): A chef puts a pizza in the oven and stands completely still for 15 minutes watching it bake before taking the next customer’s order.
  • Asynchronous (Non-Blocking): A chef puts a pizza in the oven, sets a timer, and immediately starts taking orders or chopping vegetables while the pizza bakes.

2. Synchronous Code vs. Asynchronous Code

Let’s look at a quick comparison between how standard code runs versus asynchronous code.

Standard Synchronous Code (Slow)

In this example, fetching data from two sources happens one after another. The total time will be 5 seconds (2 + 3).

import time

def fetch_data(source_id, delay):
    print(f"Fetching data from Source {source_id}...")
    time.sleep(delay)  # Blocks everything for 'delay' seconds
    print(f"Received data from Source {source_id}!")

start_time = time.time()

# Executes sequentially (one after another)
fetch_data(1, 2)
fetch_data(2, 3)

print(f"Total time taken: {time.time() - start_time:.2f} seconds")

Asynchronous Code

By using async def, await both tasks run concurrently (at the same time). The total time drops down to just 3 seconds (the time of the longest task)!

import asyncio
import time


# 1. Non-blocking coroutine function
async def fetch_data(source_id, delay):
    print(f"Fetching data from Source {source_id}...")
    await asyncio.sleep(delay)  # Non-blocking pause
    print(f"Received data from Source {source_id}!")


async def main():
    start_time = time.time()

    # 2. Run operations concurrently (at the same time)
    await asyncio.gather(
        fetch_data(1, 2),
        fetch_data(2, 3),
    )

    print(f"Total time taken: {time.time() - start_time:.2f} seconds")


# 3. Use asyncio.run() to start the event loop
asyncio.run(main())

3. The 2 Golden Rules of async / await

When starting out with asynchronous Python, just remember these three fundamental rules:

  1. async def: Place this before def to make a function asynchronous.
  2. await: Use this inside an async def function whenever you are calling a slow operation . It gives Python permission to work on other tasks while waiting.