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.
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.
result = request('url')
result = request('url')
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(
Lets see an example that emulates a synchronous approach and another example that take an asynchronous 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.
In order for us to write asynchronous code, we need an event loop.
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
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.
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.
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
Here is the same program written using different
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
One of the benefits of using
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.
Thanks for reading and I hope you enjoyed the article.