An Introduction to Asynchronous Programming in Python and the Asyncio module (Part 1)

First, before we begin talking about Asyncio, we must differentiate between Asynchronous Programming and Synchronous Programming. Synchronous Programming is essentially “normal” programming, in which code is ran sequentially from the first line to last. Asynchronous Programming, on the other hand, is essentially code that can run different operations while waiting on a function to meet a certain necessary requirement. Now that we know the difference between Synchronous and Asynchronous programming, lets look at how to set up a basic asynchronous program.

import asyncio

Coroutine: A function that is defined asynchronously (allowing it to run asynchronously)
Without coroutine:

def x():
    print('Hello World')

With coroutine:

import asyncio

async def x():
    print('Hello World')

When you call a coroutine, it won’t execute the function as expected. Instead, it returns a warning.

RuntimeWarning: coroutine 'main' was never awaited

The reason this error happens is because in Asyncio, to run a program, one must first create an event loop. The event loop is essentially the behind-the-scenes loop that runs all the asynchronous tasks in the program. To add a function to the event loop, we must use a certain syntax. Let us take a look.

import asyncio

async def x():
    print('Hello World')
asyncio.run(x())

First, we see the standard definition of a coroutine. Then, we see “asyncio.run(a())”. This line of code adds the function a() to the event loop, which runs it. Then, ‘Hello World’ is printed through a().
However, it is completely redundant to add each coroutine to the event loop individually, as this is very tedious and eliminates the whole point of using Asynchronous Programming. To unlock the full potential of Asynchronous Programming, one must use the ‘await’ keyword.
Let me explain this mysterious keyword with an example.

import asyncio

async def a():
    print('a')
    await b()
    print('c')

async def b():
    print('b')

asyncio.run(a())

Ok, lets break down what is happening here (in terms of run sequence, not line by line). on the first line, we simply import the Asyncio module. Next, we run the asynchronous program by adding the coroutine main() to the event loop and running it. Running a() prints ‘a’, and then we see the following line of code: ‘await b()’. What await does is it tells Python ‘Stop running this coroutine and run the specified coroutine instead, then after the specified coroutine is finished running, return to running this coroutine until completion.’ ‘await b()’ runs b(), which prints out ‘b’. Now, since b() has completed its execution, the program returns to running a(), which in turn prints out ‘c’ and completing the program. You see, this is what is so insanely powerful about Asynchronous Programming. The ability to run different functions at different times based on the state of other functions has an abundant amount of applications.
This concludes the article. Some other basic aspects of Asyncio (such as tasks and futures, to list two examples) will be explained in Part Two. If you would like to contact me about anything, please email me [email protected]. I hope you enjoyed!

~Josiah Mo, with love

14