loading...

Introduction to Using Async/Await in Python

code_enzyme profile image ELEZUA VICTOR ・4 min read

You probably might have heard about Async/Await keywords in other languages such as JavaScript or Dart, and how we use them for asynchronous programming. In this article we'll see how to utilize them and write asynchronous program.

What is Asynchronous Programming?

Computer programs are typically synchronously on a single thread, this implies that that one task must complete before another task runs. This can be fine in small programs that doesn't do any form of I/O.

Asynchronous programming has to do with writing programs that executes multiple tasks concurrently in order not to leave the CPU idle.

Why write Asynchronous code?

Let's assume that we write a program that fetches data from a web service, the program fetches a list of users and a list of latest posts.

def fetch_posts():
    result = request('url')
    display(result)

def fetch_users():
    result = request('url')
    display(result) 

def main():
    fetch_posts()
    fetch_users()

main()

In the pseudo-code above, the code runs synchronously. Network requests usually take time to get a response, while the program is waiting a response the CPU lies idle. Let's assume a network request takes 2 secs to resolve it means that the above program will take a little above four seconds to resolve. But if we write the program asynchronously we can run the two tasks(fetch_posts() and fetch_users()) concurrently.

Lets see an example that emulates a synchronous approach and another example that take an asynchronous approach.

Synchronous approach

If you run the above program it takes a little over three seconds to complete, that is because the fetch_users() function waits for the fetch_posts() function to complete before it starts running.

Let's look at an asynchronous way of writing the same program.

The program above runs in approximately 2 secs as opposed to the 3 secs which the synchronous approach takes. In this case the difference seems small but when working on real world applications, the difference can be huge.

So, we all know how to write synchronous programs, lets see the APIs that enable us write asynchronous code in python.

Writing Asynchronous code in python

Event Loop

In order for us to write asynchronous code, we need an event loop.

Confused

An event loop is a programming construct that waits for and dispatches events or messages in a program. The event loop is basically what executes each task in a single threaded application. The event loop can be found in the asyncio package.

Coroutines

You also need a coroutine. What is a coroutine? A coroutine in python a function or method that can pause it's execution and resume at a later point. Any task that needs to be run asynchronously needs to be a coroutine. You define a coroutine with async def. Coroutines are awaitable and can not be executed by simply calling the function. Prior to Python 3.5 the async keyword was not available in python, coroutines were created as a generator functions decorated with @asyncio.coroutine. You can read more about them here.

Let's see how they all work together.

One on the rules of writing asynchronous code is that coroutines can not contain blocking code. time.sleep(secs) is a blocking code. Using time.sleep(secs) in our asynchronous example will make it run synchronously. asyncio.sleep(secs) is not a blocking code. asyncio.sleep(secs) represents an asynchronous task that can be awaited. Trying to await a non-awaitable task results to en exception.
One common mistake people new to writing asynchronous in python make is forgetting to await coroutines and other awaitables.

As someone who has written a fair amount of JavaScript, I expected functions marked with async to behave exactly as the one in JavaScript.

Some differences between JavaScript and Python Async Functions.

JavaScript async functions run on a seperate thread and returns to the main
thread on completion but Python async functions run a single thread and only
switch to another coroutine when an asynchronous I/O operation is encountered.

Consider this program.

In this program I made the event loop switch to another coroutine after every 1000 count. When you run the program, you'll notice that the second coroutine finishes before the first. All coroutine should be awaiting something in order for your code to be asynchronous. Remember everything runs on a single thread, so it's your responsibility to manage it. You can play with the repl to get used to async/await and asyncio.

Here is the same program written using different asyncio API.

From the code above you can see that i didn't call asyncio.get_event_loop(), this is because asyncio.run() is equivalent to loop = asyncio.get_event_loop() and loop.run_until_complete.
One of the benefits of using asyncio.gather() over asyncio.wait() is the ease of getting return values of the coroutines. asyncio.gather() when awaited returns a list containing all return values.

Check out this Stackoverflow question If you want to know how to get back your return value from your coroutines.

Check this out for a summary of commonly used asyncio APIs.
asyncio documentation.

Thanks for reading and I hope you enjoyed the article.

Posted on by:

code_enzyme profile

ELEZUA VICTOR

@code_enzyme

Software Developer. Android, Flutter, JavaEE, Node.

Discussion

pic
Editor guide
 

Awesome post bro. Though you mixed gists and native code highlight.