Async for Loop in Python
This tutorial will provide a complete detail about asynchronous for
loops in Python. We will discuss the asynchronous function, asynchronous for
loop, and sleep
concepts.
Next, we will discuss the Python library asyncio
and the functions required to run asynchronous code. Finally, we will see a couple of examples in Python to fully understand the concept.
Asynchronous Function in Python
Asynchronous functions help run heavy tasks (like batch programs, maybe we are interested in running processes in the background) in parallel to other functions of the same program. It is possible to run functions in parallel when functions are not dependent on each other or not entirely dependent on each other.
A synchronous function returns control once the task is completed, whereas an asynchronous function returns control to run other functions/code in parallel and gets back control after some time. In this way, not only the heavy task is completed with many other tasks also completed in parallel.
Technically, a synchronous function blocks the control of the main
function, whereas an asynchronous function executes without blocking the main
function. This way, the main
function can run multiple asynchronous functions in parallel.
Asynchronous for
Loop in Python
For synchronous loops, they execute without any pause/giving control; however, to make them asynchronous, we have to define them in some asynchronous function. Also, we need to sleep in this process for some duration to give control to some other function.
Use the sleep
Statement
A process/thread/function may go to sleep for some time; you may consider it a break. However, the purpose is to lose control for some duration.
As a result, some other function gets the control. After some duration, the control goes back, and the function resumes.
Now, it’s time to move towards implementation in Python. We will discuss the syntax part step by step, and at the end, we will show the complete code.
Python Library and Functions
The asyncio
is a library in Python to write concurrent programs/functions using the async/await
syntax. The async
is the keyword used (to create the asynchronous functions) at the start of every function.
The syntax is:
async def fun_a(t):
Here async
is added to declare this function as an asynchronous function.
The sleep
function can suspend the execution of a coroutine for some duration. Coroutines are the processes/functions that make a pipeline structure during execution.
This function voluntarily leaves the CPU for another cooperative task through the await
keyword. The syntax of the sleep
function is:
await asyncio.sleep(1)
Note: The keyword
await
is required to call every asynchronous function, whether it is from the library or a user-defined function.
The await
keyword returns the control to the event loop. You may consider that if an asynchronous function calls with the await
command, the sleep
statement (inside the for
loop) controls the cooperative process until sleeping.
The gather
function combines multiple cooperative processes (technically making a coroutine) to run as a unit. This function returns a tuple of results in the order of functions written in the gather
call.
The syntax is:
results = await asyncio.gather(fun_a(5), fun_b(5))
Here, we are making a pipeline of fun_a
and fun_b
so they can run concurrently.
The event loop is the main ingredient of every asynchronous application. Event loops run asynchronous functions.
The get_event_loop()
method checks if set_event_loop
is not called yet, and it will create an event loop and set it as current. The syntax is:
my_loop = asyncio.get_event_loop()
If this command is executed a second time when the loop is already created, it will do nothing. However, in the first call, no loop is created; therefore, it will create an asynchronous loop.
The run_until_complete()
is used to run the event loop concurrently if the loop returned by the get_event_loop()
is asynchronous. The syntax is:
my_loop.run_until_complete(main())
This statement will run the code concurrently if the main
function is asynchronous.
Asynchronous Code
Having a clear idea about the concept and the Python library required to run asynchronous function/routine/process, it’s time to see a complete coding example:
import asyncio
async def fun_a(t):
for i in range(t):
print("fun_a", end=" ")
await asyncio.sleep(1)
return 1
async def fun_b(t):
for i in range(t):
print("fun_b", end=" ")
await asyncio.sleep(1)
return 2
async def main():
results = await asyncio.gather(fun_a(5), fun_b(5))
print(results)
my_loop = asyncio.get_event_loop()
my_loop.run_until_complete(main())
In this code, on the top (the very first line), we are importing the asyncio
library. This library has a required function for calling asynchronous functions to run concurrently; they are already discussed with syntax.
Next, we have two asynchronous functions—fun_a
and fun_b
, which we want to run concurrently. Again, we want to call asynchronous functions from the main function. Therefore, the main is also created as an asynchronous function.
Also, note that we pass 5
to our function to run loops inside it five times. So later, you can see the output and get the idea that the loop is not running completely; each goes to a sleep state and gives control to the other function.
We will repeat this point after the output.
Inside the main
function, we have called the gather
function and passed our asynchronous functions so that they can run in parallel. The gather
function returns a tuple, having two values returned from our target asynchronous functions.
We are finally coming to the last two lines. In the second last line, we called the get_event_loop
function to create a loop. In the last line, using our loop, we called the run_until_complete
function to start running our main
function asynchronously.
The output of this code is:
fun_a fun_b fun_a fun_b fun_a fun_b fun_a fun_b fun_a fun_b [1, 2]
First, note that our loops are not running completely like conventional codes; instead, both loops are running concurrently. You can see the result of both functions print
statements.
Lastly, [1, 2]
is the tuple received by the main
function from our asynchronous functions. 1
and 2
are returned by our asynchronous functions and arranged in the order we wrote these functions in the gather
function.