paint-brush
Asynchronous Programming in Python using Async IOby@pragativerma
1,667 reads
1,667 reads

Asynchronous Programming in Python using Async IO

by Pragati VermaNovember 1st, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In this article, we’ll be learning what is Async IO, its components, and a basic implementation in Python to get you started. Let’s explore the different components of AsyncIO Programming to understand how it works. We also discuss the differences between the most confusing terms - Parallelism, Concurrency, Threading, and Asyn IO in brief: **Parallelism -** It involves executing multiple tasks at a time(simultaneously). **Concurrency -** Concurrency can be defined as a much broader form of parallelism where multiple tasks can be executed in an overlapping manner. ** Asynchronism is a single-threaded and single-process implementation that gives an impression of concurrency.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Asynchronous Programming in Python using Async IO
Pragati Verma HackerNoon profile picture


Synchronous programming is a concept familiar to almost anyone starting to code as it’s the kind of code that is executed one instruction at a time. Whereas asynchronous programming is the kind of coding technique where the instructions are executed concurrently. This means that they are independent of the main program flow and are executed in the background without waiting for the completion of the previous task or instruction.


This could be helpful in a situation where let’s say you have certain independent tasks which take a lot of time to execute and might block the main flow of the program and their outputs are not dependent on each other. In such instances, asynchronous programming could not just help you to save some time but also help you in making optimal and efficient use of the resources.


When Do We Need Asynchronous Programming?

Consider a traditional web scraping program that needs to open thousands of network connections. It could open one connection, fetch the results, and then move to the next one iteratively but this could increase the latency of the program as it has to spend a lot of time to open a connection and then wait for the previous instruction to be finished.


On the other hand, asynchronous programming could help you open thousands of connections at once and lets you swap among each connection based as they finish their execution and return the results, thus, basically, it will send the request and move on to the next instruction without waiting for the execution to finish and it continues like this until all the instructions have been executed.



Source: http://www.phpmind.com/blog/2017/05/synchronous-and-asynchronous/


Asynchronous programming can be helpful in situations such as the following:

  • The program takes a lot of time to execute and return results.
  • Or the reason for the delay in the execution of the entire program because of waiting for the input and output operations and not computation.
  • Or the tasks that may require multiple input or output operations to be executed at once.


Before we dive deeper into Async IO, let’s discuss the differences between the most confusing terms in asynchronous programming - Parallelism, Concurrency, Threading, and Async IO in brief:


  • Parallelism - It involves executing multiple tasks at a time(simultaneously). It is well suited for CPU-intensive tasks and multi-processing is an example of parallelism. Read more about multi-processing and multi-threading in python here.
  • Concurrency - Concurrency can be defined as a much broader form of parallelism where multiple tasks can be executed in an overlapping manner.


Source: https://twitter.com/ohidxy/status/946110898539659264


  • Threading - A thread is an independent flow of execution. It can be essentially seen as a lightweight individual component of a process, which can run parallel. There can be multiple threads in a process, that share the same memory space, which means that they share the code to be executed and the variables declared in the program with each other. Multi-threading is suited for IO-bound tasks.
  • Async IO - Async IO is a single-threaded and single-process implementation that gives an impression of concurrency.


Having learned the difference between these commonly confused terms, let’s dive deeper into Async IO. In this article, we’ll be learning what is Async IO, its components, and a basic implementation in Python to get you started.


Async IO in Python Programming

Python3 natively supports asynchronous programming with Async IO, which is a Python library that enables to write of concurrent code by using async/await syntax.


It is also the core of many other asynchronous programming frameworks that provide high-performance networks, web servers, distributed task queues, database connection libraries, etc.


Let’s explore the different components of Async IO Programming to understand how it works.


Components of Async IO Programming

1. Coroutines: These are the functions that schedule the execution of the tasks.

2. Tasks: These are used to schedule coroutines concurrently.

3. Event Loop: This is the central mechanism that executes the coroutines until they are complete. It can be imagined as a simple while loop that monitors a coroutine and takes continuous feedback on what’s idle and looks around for the tasks that can be executed in the meantime. In Python, only one event loop can run at a time.

4. Futures: These are the results of the execution of a coroutine. This might be an exception as well.


Read more about these components here.


Now, let’s see how these components work together to execute asynchronous code running in a single thread.


It looks something like this -

  • The event loop is running in a thread.
  • It gets a task from the queue.
  • Each task calls the next step of a coroutine
  • If the current coroutine calls another coroutine, the current coroutine gets suspended and a context switch happens such that the context of the called coroutine is loaded and the context of the current coroutine is saved.
  • If the current coroutine encounters a blocking code(I/O or sleep), then the current coroutine is suspended and the control is passed back to the event loop.
  • Then the event loop gets the next task in the queue and starts to execute the above steps again until the queue gets empty.
  • Then the event loop goes back to task 1 from where it started.


How does Async IO work?

Async IO uses two keywords - async and await. Any function that is written as async def is a coroutine, for example, main() below:


import asyncio

async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')

asyncio.run(main())

# Hello ...
# ... World!


The most common way to run a coroutine is to wrap the coroutine in a asyncio.run() function that acts as an entry point to the async function and starts the event loop and runs the coroutine. The await keyword indicates to await the result of the coroutine and passes the control back to the event loop.


Now, to run the coroutines concurrently, we’ll need to define tasks. We use asyncio.create_task() to schedule a coroutine for execution in the event loop.


Notice that the task is different from the await keyword that blocks the entire coroutine until the operation completes with a result.


For example, the following code snippet illustrates how to create tasks that schedule and execute a coroutine(call_api()) in this case:


import asyncio
import time


async def call_api(message, result=1000, delay=3):
    print(message)
    await asyncio.sleep(delay)
    return result


async def main():
    start = time.perf_counter()

    task_1 = asyncio.create_task(
        call_api('Get stock price of GOOG...', 300)
    )

    task_2 = asyncio.create_task(
        call_api('Get stock price of APPL...', 300)
    )

    price = await task_1
    print(price)

    price = await task_2
    print(price)

    end = time.perf_counter()
    print(f'It took {round(end-start,0)} second(s) to complete.')


asyncio.run(main())


The output will be something like this:


Get stock price of GOOG...
Get stock price of APPL...
300
300
It took 3.0 second(s) to complete.


Conclusion

In this article, we discussed asynchronous programming in Python with the built-in Async IO module. We also learned the basic components of Async IO such as coroutines, tasks, and futures, and how to implement these in a simple program to attain concurrency.


This was all for this article. I hope it was helpful.