Python  

How to Use Async and Await in Python for Better Performance

Introduction

Python is one of the most widely used programming languages in the world, employed by developers for a range of applications, including web development, data analysis, and automation. However, one common issue developers face is slow performance when programs wait for tasks such as API responses, file downloads, or database queries. This happens because traditional Python code runs one task at a time.

To make programs faster and more efficient, Python provides two special keywords: async and await. These help your program perform multiple tasks simultaneously without waiting for one task to complete before starting another. In this article, we’ll explore what async and await mean, how they work, and how you can use them to improve your Python app’s performance — even if you’re a beginner.

What Is Asynchronous Programming in Python?

When you write standard Python code (called synchronous code), each line runs one after the other. For example, if your program is downloading a file, it must complete the download before proceeding to the next task. This approach works fine for simple tasks but can slow things down when dealing with operations that take time, like reading from the internet or a database.

Asynchronous programming enables your program to initiate a task, run it in the background, and proceed with other tasks while waiting for it to complete. Think of it like cooking multiple dishes at once — while one dish is in the oven, you can start chopping vegetables for the next one. This way, you save time and make better use of your computer’s resources.

With asynchronous programming, Python becomes capable of handling multiple tasks simultaneously, which helps improve performance and efficiency, especially in web development and data-intensive applications.

Understanding Async and Await

The keywords async and await were introduced in Python 3.5 to make asynchronous code easier to write and understand.

  • async is used to define an asynchronous function. It tells Python that this function will perform tasks that might take time to complete.

  • await is used inside an async function to pause that function’s execution until a specific operation finishes.

Here’s a simple example:

import asyncio

async def greet():
    print("Hello...")
    await asyncio.sleep(2)
    print("World!")

asyncio.run(greet())

Explanation:

  • The keyword async def tells Python that greet() is an asynchronous function.

  • The line await asyncio.sleep(2) pauses for two seconds without blocking other code.

  • The function asyncio.run() is used to start and run the asynchronous function.

Unlike time.sleep(), which stops the whole program, asyncio.sleep() only pauses that specific function, allowing other functions to continue running.

Running Multiple Async Tasks Together

The real power of async and await comes when you run multiple tasks at the same time. Instead of completing one task and then moving to the next, Python can perform all of them concurrently.

Example:

import asyncio

async def download_file(file_name):
    print(f"Downloading {file_name}...")
    await asyncio.sleep(2)
    print(f"Finished downloading {file_name}")

async def main():
    tasks = [
        download_file("file1.txt"),
        download_file("file2.txt"),
        download_file("file3.txt"),
    ]
    await asyncio.gather(*tasks)

asyncio.run(main())

How it works:

  • The program starts all three downloads at once.

  • While one file is downloading, others are being processed simultaneously.

  • The asyncio.gather() function collects and runs all async tasks together.

This method can save a lot of time when working with multiple files, API requests, or database queries.

Real-Life Example: Making Multiple API Calls

Let’s say you want to fetch data from several APIs. Normally, you would make one request, wait for it to complete, then move to the next. With async and await, you can send all the requests at once and handle the responses as they arrive.

Example using the aiohttp library:

import asyncio
import aiohttp

async def fetch_data(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def main():
    urls = [
        'https://jsonplaceholder.typicode.com/posts/1',
        'https://jsonplaceholder.typicode.com/posts/2',
        'https://jsonplaceholder.typicode.com/posts/3'
    ]

    tasks = [fetch_data(url) for url in urls]
    results = await asyncio.gather(*tasks)

    for result in results:
        print(result[:100])

asyncio.run(main())

Why it’s better:

  • Instead of waiting for each request, all API calls happen together.

  • Your app responds faster and handles more requests without extra servers.

This is especially useful for web scraping, data analysis, and Python web applications that deal with multiple APIs.

When to Use Async and Await in Python

You should use async and await when your program performs I/O-bound tasks, meaning tasks that spend time waiting for something else, such as:

  • Reading or writing files

  • Making API or HTTP requests

  • Interacting with databases

  • Communicating with external servers

However, async is not suitable for CPU-bound tasks like performing complex calculations or processing images. For those, you should use Python’s multithreading or multiprocessing features.

Common Mistakes to Avoid

Here are a few common mistakes beginners make when using async and await:

  1. Using await outside an async function – You can only use await inside a function defined with async def.

  2. Mixing blocking functions with async code – Avoid using functions like time.sleep() inside async functions. Use asyncio.sleep() instead.

  3. Not using gather() for multiple tasks – Without asyncio.gather(), your tasks will run one after another, defeating the purpose of async.

Benefits of Using Async and Await

✅ Faster performance – Handle many tasks at the same time.
✅ Better scalability – Ideal for web servers, APIs, and applications that handle many users.
✅ Cleaner code – Easier to read and maintain compared to threading or callbacks.
✅ Efficient resource usage – Reduces CPU load and memory usage.

Summary

Using async and await in Python is one of the best ways to make your applications faster and more efficient, especially for I/O-heavy tasks like API calls, web scraping, and file handling. Asynchronous programming allows your program to handle multiple operations simultaneously without blocking the main thread. By understanding how async and await work and avoiding common mistakes, you can create Python programs that are not only faster but also cleaner and more scalable.