top of page

Async Programming and Asyncio Library in Python

Asynchronous programming has been gaining popularity in recent years due to its ability to handle I/O bound tasks efficiently. It allows multiple tasks to be executed concurrently without blocking the main program, resulting in improved performance and resource utilization. Python, being a popular programming language, has a built-in library called asyncio that provides the necessary tools for asynchronous programming. In this article, we will explore the basics of async programming and the asyncio library in Python.


Async Programming Basics

Asynchronous programming is a programming paradigm that allows multiple tasks to be executed concurrently without blocking the main program. This is achieved by executing tasks in a non-blocking manner and using callback functions to handle the results. As a result, the program can continue to execute other tasks while waiting for the results of the async task. This approach is particularly useful for I/O bound tasks, such as reading and writing to files, network communication, and database access.



Synchronous (or blocking) programming is the traditional way of writing programs, where each line of code is executed sequentially and the program waits for each operation to complete before moving on to the next one. In contrast, asynchronous (or non-blocking) programming allows multiple tasks to be executed concurrently without blocking the main thread, allowing the program to make progress even while waiting for I/O operations to complete.


Here are some of the main differences between synchronous and asynchronous programming in Python:

  1. Blocking vs non-blocking I/O: In synchronous programming, I/O operations such as reading from a file or making a network request block the main thread until the operation is complete. In asynchronous programming, I/O operations are non-blocking, allowing the program to continue executing other tasks while waiting for the I/O operation to complete.

  2. Sequential vs concurrent execution: In synchronous programming, each line of code is executed sequentially, one after the other. In asynchronous programming, multiple tasks can be executed concurrently, allowing the program to make progress even while waiting for I/O operations to complete.

  3. Coroutines vs functions: In asynchronous programming, coroutines are used instead of traditional functions. Coroutines can be paused and resumed at any point using the await keyword, allowing the program to switch between tasks as needed.

  4. Event loop vs call stack: In asynchronous programming, an event loop is used to manage the execution of coroutines and schedule tasks to be executed. In synchronous programming, the call stack is used to manage the execution of functions and keep track of the program state.


Asyncio Library

In Python, async programming is implemented using the asyncio library. Asyncio is a library in Python that provides tools for writing asynchronous, concurrent, and event-driven programs. It is included in the standard library starting from Python 3.4 and has become a popular choice for building high-performance web applications and network servers.


Here are some of the key concepts and terminology used in async programming with asyncio:

  1. Event loop: The event loop is the core of the asyncio library. It manages the execution of coroutines and schedules tasks to be executed. The event loop continuously runs, waiting for coroutines to complete or become suspended.

  2. Coroutines: Coroutines are functions that use the async keyword and can be paused and resumed at any point using the await keyword. They allow multiple tasks to be executed concurrently without blocking the main thread.

  3. Futures: Futures are objects that represent the result of a coroutine that has not yet completed. They can be used to schedule a task to be executed in the future or to wait for a coroutine to complete.

  4. Tasks: Tasks are objects that represent a coroutine that has been scheduled to run by the event loop. They can be used to start a coroutine and track its progress.

  5. Synchronization primitives: The asyncio library provides several synchronization primitives, such as locks and semaphores, that can be used to coordinate the execution of coroutines.

  6. Protocols: Protocols are interfaces that define the communication between different parts of an application. The asyncio library provides several protocols for building network servers, such as the asyncio.Protocol and asyncio.StreamReaderProtocol classes.

  7. Transports: Transports are objects that handle the low-level communication between different parts of an application. The asyncio library provides several transports for building network servers, such as the asyncio.Transport and asyncio.WriteTransport classes.

Here is an example of a simple async program that uses coroutines and the event loop:

import asyncio

async def my_coroutine():
    print("Coroutine started")
    await asyncio.sleep(1)
    print("Coroutine resumed")

async def main():
    print("Main program started")
    task = asyncio.create_task(my_coroutine())
    await task
    print("Main program resumed")

asyncio.run(main())

In this example, we define a coroutine my_coroutine() that prints a message, sleeps for 1 second, and then prints another message. We then define the main() coroutine that creates a task for my_coroutine() using asyncio.create_task(), awaits the task to complete, and then prints a message.


Finally, we run the main() coroutine using asyncio.run(). The event loop runs the main() coroutine, which creates a task for my_coroutine() and awaits its completion.


How to Implement Async programming

Here are the basic steps to implement asynchronous programming in Python using the asyncio library:


STEP 1: Define your coroutine functions: A coroutine is a special type of function that can be paused and resumed at any point using the await keyword. Define your coroutine functions using the async def syntax.


STEP 2: Create an event loop: The event loop is the core of the asyncio library. Create an event loop object using the asyncio.get_event_loop() function.


STEP 3: Schedule your coroutines: Use the asyncio.create_task() function to schedule your coroutines to run on the event loop. This will start the coroutines and return a Task object that can be used to track their progress.


STEP 4: Run the event loop: Call the event_loop.run_until_complete() method, passing in the Task objects you created in step 3. This will run the event loop and execute your coroutines.


Here is an example implementation of these steps:

import asyncio

async def coroutine1():
    print("Starting coroutine 1")
    await asyncio.sleep(2)
    print("Coroutine 1 finished")

async def coroutine2():
    print("Starting coroutine 2")
    await asyncio.sleep(1)
    print("Coroutine 2 finished")

async def main():
    print("Starting main coroutine")
    task1 = asyncio.create_task(coroutine1())
    task2 = asyncio.create_task(coroutine2())
    await asyncio.gather(task1, task2)
    print("Main coroutine finished")

if __name__ == "__main__":
    event_loop = asyncio.get_event_loop()
    event_loop.run_until_complete(main())

In this example, we define two coroutines coroutine1() and coroutine2() that simulate some time-consuming operations using the asyncio.sleep() function. We also define a main() coroutine that schedules these two coroutines using the asyncio.create_task() function and waits for them to complete using the asyncio.gather() function.


Finally, we create an event loop using asyncio.get_event_loop() and run the main coroutine using event_loop.run_until_complete(). When we run this program, we should see output similar to the following:

Starting main coroutine
Starting coroutine 1
Starting coroutine 2
Coroutine 2 finished
Coroutine 1 finished
Main coroutine finished

This shows that the two coroutines were executed concurrently and the main coroutine was able to continue running while they were waiting for their time-consuming operations to complete.


Advantages of Asyncio

Here are some of the main advantages:

  1. Improved performance: Asynchronous programming allows multiple tasks to be executed concurrently without blocking the event loop. This means that asyncio programs can be more efficient and faster than their synchronous counterparts.

  2. Simplified code: Asynchronous programming with asyncio simplifies code by avoiding the need for complicated threading or multiprocessing. Developers can use coroutines and futures to write code that looks similar to synchronous code, making it easier to read and maintain.

  3. Scalability: asyncio is designed to be highly scalable, which means that it can handle large numbers of concurrent connections or requests. This makes it ideal for building high-performance web applications or network servers.

  4. Cross-platform support: Asyncio is supported on all major platforms, including Windows, Linux, and macOS, making it a versatile tool for developers.

  5. Compatibility with other libraries: asyncio is compatible with many popular Python libraries, such as aiohttp and aiofiles, making it easy to integrate with existing codebases.


Examples of using Asyncio


Example 1: Making HTTP requests with aiohttp

The aiohttp library is a popular library for making HTTP requests asynchronously in Python. Here's an example of how to use it with asyncio:

import asyncio
import aiohttp

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

async def main():
    urls = ['https://www.google.com', 'https://www.facebook.com', 'https://www.twitter.com']
    tasks = []
    for url in urls:
        tasks.append(asyncio.ensure_future(fetch(url)))

    responses = await asyncio.gather(*tasks)
    for response in responses:
        print(response[:100])

asyncio.run(main())

In this example, we define a coroutine function fetch that makes an HTTP GET request using the aiohttp library. We then define a main coroutine that creates a list of URLs to fetch, creates a task for each URL, and runs them concurrently using asyncio.gather(). The results are then printed to the console.


Example 2: Scraping websites with asyncio and BeautifulSoup

The BeautifulSoup library is a popular library for parsing HTML and XML documents in Python. Here's an example of how to use it with asyncio to scrape multiple websites concurrently:

import asyncio
import aiohttp
from bs4 import BeautifulSoup

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

async def scrape(url):
    html = await fetch(url)
    soup = BeautifulSoup(html, 'html.parser')
    title = soup.find('title').text
    print(f'{url}: {title}')

async def main():
    urls = ['https://www.google.com', 'https://www.facebook.com', 'https://www.twitter.com']
    tasks = []
    for url in urls:
        tasks.append(asyncio.ensure_future(scrape(url)))

    await asyncio.gather(*tasks)

asyncio.run(main())

In this example, we define a coroutine function scrape that uses the fetch function from the previous example to retrieve the HTML content of a website, parses it using BeautifulSoup, and prints the title to the console. We then define a main coroutine that creates a task for each URL and runs them concurrently using asyncio.gather().


Example 3: Creating a simple chatbot using asyncio

Here's an example of how to use asyncio to create a simple chatbot that responds to user input:

import asyncio

async def chatbot():
    while True:
        user_input = input('You: ')
        if user_input.lower() == 'hi':
            print('Bot: Hello!')
        elif user_input.lower() == 'bye':
            print('Bot: Goodbye!')
            breakelse:
            print('Bot: Sorry, I don\'t understand.')

asyncio.run(chatbot())

In this example, we define a coroutine function chatbot that uses an infinite loop to read user input from the console and respond accordingly. The asyncio.run() function is used to run the chatbot coroutine.


Conclusion

Async programming with asyncio in Python is a powerful tool for writing efficient and scalable programs. It allows developers to write concurrent code without the need for complex threading or multiprocessing, making it easier to read and maintain. The asyncio library provides a number of tools for managing coroutines, futures, and the event loop, which are essential components of any async program. By using the examples provided in this article, developers can get started with async programming in Python and take advantage of the benefits it offers. Whether you're making HTTP requests, scraping websites, or building chatbots, asyncio can help you write better code with less hassle.

0 comments

Recent Posts

See All

Comments


bottom of page