Skip to Content
Course content

208: Introduction to asyncio

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're a barista at a busy coffee shop. You have a customer who wants a latte. To make it, you start the espresso machine, but the machine takes 30 seconds to pull the shot. Now, you have a choice: you can stand there and stare at the machine for 30 seconds, doing absolutely nothing, or you can take the next customer's order while the coffee is dripping.

If you stare at the machine, you're working "synchronously." You're blocked until that one task finishes. If you take other orders, you're working "asynchronously." You've initiated a task that takes time, and instead of wasting your life waiting for it, you switch to something else until the machine "dings" to let you know it's done. This is exactly what asyncio does for your Python code.

Stopping the Stare-Down

In standard Python, when you call a function that fetches data from a website, your whole program freezes. It just sits there. If you have to fetch data from ten different websites, and each takes one second, your program takes ten seconds. That's a lot of staring at the espresso machine.

With asyncio, we tell Python: "This task is going to involve waiting. Go ahead and do other things, and come back to this when the data actually arrives."

Turning Functions into Coroutines

To do this, we change how we define our functions. We use async def instead of just def. This creates a "coroutine." If you try to call a coroutine like a normal function, you'll notice it doesn't actually run the code inside—it just returns a coroutine object. I've seen plenty of developers get frustrated here, thinking their code is broken, but it's actually just Python asking you how you intend to schedule this task.

import asyncio

async def brew_coffee():
    print("Starting the espresso machine...")
    # This is where the 'waiting' happens
    await asyncio.sleep(1) 
    print("Coffee is ready!")

# This won't work: brew_coffee() 
# You need an event loop to run it:
asyncio.run(brew_coffee())

The Magic of the Await Keyword

The await keyword is the most important part of this whole puzzle. It's you, the barista, saying: "I'm pausing this specific task here because I'm waiting on something. Feel free to go do other work."

You can only use await inside an async function. When Python hits an await line, it pauses the execution of that function and jumps back to the event loop to see if any other tasks are ready to make progress.

Handling a Crowd with Gather

Running one async function is fine, but the real power comes when you have a dozen tasks. If you just await them one by one, you're back to square one—you're still waiting for the first coffee to finish before you even start the second one. To actually run them concurrently, we use asyncio.gather().

Check out the difference here. We're simulating fetching data from three different API endpoints:

import asyncio
import time

async def fetch_data(source, delay):
    print(f"Fetching from {source}...")
    await asyncio.sleep(delay)
    print(f"Finished {source} after {delay}s")
    return f"{source} data"

async def main():
    start_time = time.perf_counter()
    
    # This schedules all three calls to start at roughly the same time
    results = await asyncio.gather(
        fetch_data("Users-API", 2),
        fetch_data("Products-API", 1),
        fetch_data("Orders-API", 1.5)
    )
    
    end_time = time.perf_counter()
    print(f"Results: {results}")
    print(f"Total time elapsed: {end_time - start_time:.2f} seconds")

asyncio.run(main())

Notice that the total time isn't 4.5 seconds (2 + 1 + 1.5). It's only 2 seconds—the length of the longest task. Python started the Users-API call, hit the await, immediately jumped to start the Products-API call, hit the await, and so on. It's effectively multitasking.




📋 Practical Task

Build an Asynchronous Website Status Checker

Your task is to build a script that checks the "health" of multiple simulated servers. Since we don't want to actually spam real websites, you'll simulate the network latency using asyncio.sleep().

Requirements:

  • Create an async function called check_server(server_name, response_time). It should print "Checking [server_name]...", wait for the response_time, and then return a string saying "[server_name] is UP".
  • Create a main coroutine that uses asyncio.gather to check the following servers simultaneously:
    • "Auth-Server" (takes 1.2 seconds)
    • "Database-Cluster" (takes 0.5 seconds)
    • "Payment-Gateway" (takes 2.0 seconds)
    • "Cache-Layer" (takes 0.8 seconds)
  • Print the final list of results and the total time the script took to run.
  • Verify that the total elapsed time is approximately 2 seconds, not the sum of all response times.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.