async/await in Python
Published: 2024-03-12 00:12:08

<p>The <code>async </code>and <code>await </code>keywords are used to&nbsp;manage <a href="https://www.pynerds.com/asynchronous-programming-with-python/">asynchronous </a>tasks.&nbsp;</p>

<p>Asynchronous programming allows multiple tasks to be processed simultaneously without blocking other tasks. In Python, it is achieved through the use of&nbsp;coroutines.</p>

<p>The <code>async </code>keyword is used to define a function as an asynchronous coroutine, and the <code>await </code>keyword is used inside of a coroutine function&nbsp;to wait for the execution of another&nbsp;coroutine.</p>

<h2>What are coroutines?</h2>

<p>Coroutines are just like regular functions with the additional capability of&nbsp; pausing and resuming their execution at specific points. This allows for non-blocking behavior, where the execution of a&nbsp;coroutine can be suspended in case of delays or to deliberately&nbsp;allow other code to run before resuming its execution.</p>

<p>Coroutine&nbsp;are very closely related to <a href="https://www.pynerds.com/generator-functions-in-python/">generator functions</a>. In fact,&nbsp;prior to&nbsp;the introduction of <code>async </code>and <code>await&nbsp;</code>in Python 3.5,&nbsp;generator functions were used in a special way to implement coroutines.</p>

<p>With python 3.5 and later versions,&nbsp;we can now create coroutines more conveniently&nbsp;and natively using the two keywords, <code>async </code>and <code>await</code>.</p>

<div>
<ol>
	<li><code>async&nbsp;</code>creates a coroutine&nbsp;function.</li>
	<li><code>await&nbsp;</code>suspends&nbsp;a coroutine&nbsp;to&nbsp; allow another coroutine to be executed.&nbsp;</li>
</ol>
</div>

<h2>Define&nbsp;coroutine functions with <code>async</code></h2>

<p>Defining coroutine functions is much like&nbsp;defining regular functions. Consider the following example:</p>

<div class="autorun example">
<p>Define&nbsp; a regular function</p>

<pre>
<code class="language-python">def add(a, b):
    print(f'{a} + {b} = {a + b}')</code></pre>
</div>

<p>With a regular function, we use the <code>def </code>keyword in the declaration, with a coroutine&nbsp;function, we use<code>&nbsp;async def</code>&nbsp;instead of just <code>def</code>.</p>

<div class="autorun example">
<p>Define a coroutine function</p>

<pre>
<code class="language-python">async def add(a, b):
    print(f'{a} + {b} = {a + b}')</code></pre>
</div>

<h4>Calling a coroutine function</h4>

<p>To call a regular function, you simply use its name followed by parenthesis with the necessary arguments.&nbsp;</p>

<div class="autorun example">
<p>Call a regular function</p>

<pre>
<code class="language-python">def add(a, b):
    print(f'{a} + {b} = {a + b}')

add(10, 20)</code></pre>
</div>

<p>Unlike with regular functions, calling a coroutine function does not immediately execute the code inside the function. Instead, it creates a coroutine object.</p>

<div class="autorun example">
<pre>
<code class="language-python">async def add(a, b):
    print(f'{a} + {b} = {a + b}')

#call a coroutine
coro = add(10, 20) #creates a coroutine object
print(coro)</code></pre>
</div>

<p>To actually run the code inside the coroutine function, you need to await it in what is referred to as&nbsp;<strong>an event loop</strong>. The most direct way to do this is by using the <code>run()</code> function in the <code>asyncio </code>module of the standard library.</p>

<div class="autorun example">
<pre>
<code class="language-python">import asyncio

async def add(a, b):
    print(f'{a} + {b} = {a + b}')

#create a coroutine object
coro = add(10, 20) 

#run the coroutine
asyncio.run(coro) #executes the code in the coroutine</code></pre>
</div>

<p>The <code>asyncio.run() </code>function&nbsp;creates an event loop,&nbsp; runs the specified coroutine and returns the result of the coroutine. It is designed to be used for&nbsp;running the main function of an asynchronous&nbsp;application or to run a simple one-off coroutine.</p>

<p>Without <code>asyncio.run() </code>you would be needed to manually,&nbsp;initialize and manage the event loop, create and schedule tasks, and wrap the code in a coroutine. This would be more complicated and error-prone compared to using<code> asyncio.run()</code>, which handles all of these steps automatically.</p>

<div class="autorun example">
<p>more examples</p>

<pre>
<code class="language-python">import asyncio

async def add(a, b):
    print(f'{a} + {b} = {a + b}')

asyncio.run(add(50, 30))
asyncio.run(add(50, 50))
asyncio.run(add(90, 10))</code></pre>
</div>

<h2>Suspending coroutines&nbsp;with <code>await</code></h2>

<p>The <code>await </code>keyword is used to pause the execution of a coroutine until another inner coroutine is&nbsp;finished.&nbsp;Once the awaited coroutine has finished, the original coroutine will resume its execution from where it left off.</p>

<div class="syntax">
<pre>
<code class="language-python">await &lt;coro()&gt;</code></pre>
</div>

<p>The <code>await </code>keyword takes a single argument,<code> coro()</code>,&nbsp;a&nbsp;coroutine object to be awaited.</p>

<p>Consider the following example.</p>

<div class="autorun example">
<p>await a coroutine&nbsp;</p>

<pre>
<code class="language-python">import asyncio

#prints even numbers from 0-n
async def display_evens(n):
    for i in range(n):
        if i % 2 == 0:
            print(i)

async def main():
      print("Started!")
      await display_evens(10) #await display_evens()
      print('Finished!')

#run main
asyncio.run(main())</code></pre>
</div>

<p>In the above example, when the <code>await </code>statement is encountered, the execution of main is suspended until the <code>display_evens()</code> coroutine is fully executed. Multiple coroutines can be awaited as necessary.</p>

<div class="autorun example">
<p>await multiple coroutines</p>

<pre>
<code class="language-python">import asyncio

#prints even numbers from 0-n
async def display_evens(n):
    for i in range(n):
        if i % 2 == 0:
            print(i)
        await asyncio.sleep(0.01)#cause a small delay 

#prints even numbers from 0-n
async def display_odds(n):
    for i in range(n):
        if i % 2 == 1:
            print(i)
        await asyncio.sleep(0.01)#cause a small delay

async def main():
      print("Started!")
      await display_evens(10)
      await display_odds(10) 
      print('Finished!')

#run main
asyncio.run(main())</code></pre>
</div>

<p>In the above example, the awaited coroutines, <code>display_evens()</code> and <code>display_odds()</code> are executed one after the other. In the following section, we will see how to make awaited coroutines to be executed simultaneously.</p>

<h2>Executing awaited&nbsp;coroutines&nbsp;in parallel.</h2>

<p>The primary goal&nbsp;of asynchronous programming is to execute multiple&nbsp;tasks&nbsp;in parallel. This means that while one task is executing, another task can start and run simultaneously.</p>

<p>To run multiple coroutines in parallel and in a non-blocking manner, we can combine them into a single awaitable object using the <code>asyncio.gather()</code> function, then await the returned object.</p>

<div class="autorun example">
<p>execute multiple coroutines</p>

<pre>
<code class="language-python">import asyncio

#prints even numbers from 0-n
async def display_evens(n):
    for i in range(n):
        if i % 2 == 0:
            print(i)
        await asyncio.sleep(0.01)#cause a small delay 

#prints even numbers from 0-n
async def display_odds(n):
    for i in range(n):
        if i % 2 == 1:
            print(i)
        await asyncio.sleep(0.01)#cause a small delay

async def main():
      print("Started!")
      await asyncio.gather(display_evens(10), display_odds(10)) #gathered coroutines will run in parallel
      print('Finished!')

#run main
asyncio.run(main())</code></pre>
</div>

<p>If you observe the above output, you will see that both <code>display_evens() </code>and <code>display_odds()</code> are executed simultaneously. The outputs from the two functions are emitted alternately making it look as if it was just a single function printing all the integers.</p>

<h2>Conclusion</h2>

<div>
<ul>
	<li>Asynchronous programming makes it possible to execute tasks in parallel.</li>
	<li>A coroutine is just like a regular function except that it is capable of pausing its execution and resuming later on at the same point.</li>
	<li>The <code>async </code>keyword creates coroutine objects.</li>
	<li>The <code>await </code>keyword suspends the execution of a coroutine until an inner&nbsp;coroutine is executed fully.</li>
	<li>The <code>asyncio.run() </code>function eases event loop management. It executes a single coroutine.</li>
	<li>The<code> asyncio.gather() </code>function, combines multiple coroutines into a single awaitable object.&nbsp;</li>
</ul>
</div>

<div class="related"><h3>Related articles</h3><ul><li><p><a class="text-success" href="https://www.pynerds.com/python-for-loops/">Python for loops</a><li><p><a class="text-success" href="https://www.pynerds.com/if-statement-in-python/">if statement in python</a><li><p><a class="text-success" href="https://www.pynerds.com/with-statement-in-python/">with statement in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/tryexcept-statement-in-python/">try/except statement in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/pass-statement-in-python/">pass statement in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/assert-statement-in-python/">Assert statement in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/introduction-to-concurrent-programming-in-python/">Introduction to concurrent programming in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/multithreading-in-python/">Multithreading in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/multiprocessing-in-python/">Multiprocessing in Python</a><li><p><a class="text-success" href="https://www.pynerds.com/asynchronous-programming-with-python/">Asynchronous Programming with Python</a></ul></div>