How to implement async-await in Python

The asyncio module in Python

The asynchronous way of programming is very popular in JavaScript. In NodeJS, you have built-in support to implement the async functionality. However, in Python, building async applications is just now supported with the asyncio package.

asyncio is a style of concurrent programming, not parallel programming. It is more closely aligned with the concept of threading than with multiprocessing, although it is very much distinct from both of them. asyncio uses cooperative multitasking.

Cooperative multitasking, also known as non-preemptive multitasking, is a type of computer multitasking process. In this approach, there is no context switching among processes initiated by the operating system. Rather, the processes themselves voluntarily take up the control periodically or when the CPU is idle in order to enable multiple applications to run simultaneously. The name of this approach is “cooperative” because all the programs need to cooperate for the entire scheduling scheme to be successful.

async keyword

The async keyword is put in front of a function declaration to turn it into an asynchronous function. An asynchronous function is a function that knows how to expect the possibility that an await keyword will be used to invoke the asynchronous code.

The difference between a synchronous function and an asynchronous function is shown below:

def func():
return "Hello"
print(func())

After running the above code, you can see the output as Hello. Nothing special, right?

But, what if we turn this into an asynchronous function? Let’s look in the code below:

async def func():
return "Hello"
print(func())

Now, running the function returns a coroutine. This is one of the traits of async functions, i.e., their return values are guaranteed to be converted to coroutines.

A coroutine is a function that can suspend its execution before reaching the return statement, where it can then pass the control to another coroutine to enable multiple applications to be run. In simpler words, Coroutines are basically functions whose execution one can pause.

So, the async keyword is added to functions to tell them to return a coroutine rather than directly returning the value.

await keyword

The advantage of an asynchronous function only becomes significant when you combine it with the await keyword. It should also be noted that the await keyword only works inside async functions.

The await keyword can be put in front of any async coroutine-based function to pause your code on that line until the coroutine fulfills. Then, return the resulting value.

In simpler words, you can think of the await keyword as the place where it is safe for one coroutine to move to another coroutine. It also means that you are waiting for some IO to complete.

Let’s see an example below to better understand:

import asyncio
async def main():
await asyncio.sleep(4)
await asyncio.sleep(2)
return "Hello"
print(asyncio.run(main()))

Explanation:

  • On line 1, we imported the asyncio package.
  • On line 3, we defined the asynchronous function main().
  • On line 4 and 5, we used the await keyword. This tells us that we can switch to another coroutine (if present) for execution. Right now, we have two independent coroutines that are being called (asyncio.sleep() is an async function). There is no coroutine switching happening because we haven’t gathered all the coroutines.
  • On line 8, we run out the async function by using the run() method. This is how an async function is executed. If you try to execute without the run() method, your output will be a coroutine object.

Free Resources