Geek Culture
Published in

Geek Culture

An introduction to asyncio in python

Hello asyncio world!

image source: https://dock2learn.com/tech/asyncio-in-python/

Introduction:

Asyncio is a tool for concurrent programming in python which is more light weight than threads and multi-processing. This works based on a event-loop which executes a collection of tasks and the significance is , these tasks have the liberty to choose when to yield control back to the event loop.

Target Users of asyncio:

The asyncio library is targeted for both

  1. End-user developers
  2. Framework developers

We fall into the first category, where we build applications that will be used by some user. For instance, the application could be fastapi application.

Framework developers are the ones who write libraries in python that will be consumed by end-user application developers like us.

Hello asyncio World!

The steps to write an program is as follows

  1. Create an event loop.
  2. Call async/await functions which are coroutines.
  3. Create a task to run on the loop.
  4. Wait for the tasks to complete.
  5. Close the loop.

Now, let’s get started with our first asyncio program.

import asyncio, time


async def hello_world():
print(time.ctime())
await asyncio.sleep(1.0)
print(time.ctime())


asyncio.run(hello_world())

Output:

Tue Feb 8 09:31:51 2022 
Tue Feb 8 09:31:52 2022

Now, we have introduced two new keywords async and await. Any python function declared with async def is called a coroutine function.

The await keyword always takes a parameter which is an awaitable. The awaitable could either be a

  1. Coroutine (A function executed as a result of async def).
  2. Any object that implements the special method __await__.

Well, we discussed few steps to write an asyncio program. However, the hello world program does not quite match them. Let's peel the layers one by one. What exactly happens under the hood of asyncio.run().

Under the hood of asyncio.run():

To understand what exactly asyncio.run() does, let's rewrite the hello world asyncio program without using run() method.

https://media.giphy.com/media/du8jrWgg9A9XdoIRJ7/giphy.gif
import asyncio, time


async def hello_world():
print(time.ctime())
await asyncio.sleep(1.0)
print(time.ctime())


loop = asyncio.get_event_loop()
task = loop.create_task(hello_world())
loop.run_until_complete(task)
loop.close()

Output:

Tue Feb 8 09:31:51 2022 
Tue Feb 8 09:31:52 2022

This program does exactly the same as the first one except that this is little more comprehensive. We don’t use the run() method to execute the coroutine. Instead we create an event loop and run tasks as part of the event loop.

loop = asyncio.get_event_loop()

This step gets an event loop. Why do we need one? The answer is simple. Coroutines cannot be executed by calling them directly like regular python functions. They need an event loop to run. This is how we get one ☝️ . Calling get_event_loop() anywhere outside a coroutine always gives the same instance.

task = loop.create_task(hello_world())

To execute a coroutine, creating a task is mandatory. Without this , the coroutine will never be executed. The create_task schedules the coroutine to be run on an event loop. This will return a task object. The returned value can be extracted using task.result() where task is the task object.

loop.run_until_complete(task)

This is a blocking call and will run until the coroutine is completed. asyncio.run() internally calls this method and blocks the main thread similarly.

loop.close()

This is the final step and close() should be called on a stopped loop. A closed loop cannot be reinstated. asyncio.run() creates an event loop for every run and takes care of the shut down as well.

Summary:

I hope this article would have helped you to take your baby steps into the python asyncio world.

This is just an introduction to asyncio programming. I shall discuss more in my future articles about event loop, using context managers with asyncio etc. Any asyncio program should follow the above steps and it would be easy to understand asyncio library better.

Having said that, asyncio.run() is what we will be using mostly in our day to day programming. The steps were to understand what happens internally.

Caveats:

  1. asyncio does not make your program any faster. Get over it! It just avoids the context-switching that threads have to do and race-conditions.
  2. asyncio does not remove GIL. It just doesn't have to deal with it as it is single threaded.
  3. asyncio completely eliminates all race-conditions. Well! not really. It could avoid the race conditions that multithreaded programs may introduce such as shared memory access inside the same process. The main advantage is we exactly know where there are control transfers due to the await keyword which makes debugging easier.

Originally published at https://dock2learn.com on February 8, 2022.

--

--

A new tech publication by Start it up (https://medium.com/swlh).

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store