Asynchronous programs can run multiple tasks simultaneously without waiting for the result of the time taking tasks. It gives multiple tasks the ability to run in an overlapping manner.

In Python, you can create and call asynchronous tasks/functions using one of the following ways.

Use threading for Asynchronous Function

Use threading module to asynchronouly run a function on thread other than the main.

import threading
import time

def main():
    print('Hello ...')
    time.sleep(1)
    print('... World!')

th = threading.Thread(target=main, daemon=True)
th.start()

# Block
th.join()

Here the th.join() statement blocks the current (i.e. main) thread until the of th thread completes or terminate. If it is not specified, the main thread without waiting for the th thread will terminate and there will be no output.

Use asyncio for Asynchronous Function

From Python 3.5+, you can use the asyncio library to write the concurrent running function using the async/await syntax.

import asyncio

async def main():
    print('Pencil ...')
    await asyncio.sleep(1)
    print('... Programmer!')

loop = asyncio.get_event_loop()
loop.run_until_complete(main())   #block
loop.close()

Here the main method is a coroutine. This is because it is declared with the async def statement.

From Python 3.7+, you can use the asyncio.run() method to run the coroutine.

import asyncio

async def main():
    print('Pencil ...')
    await asyncio.sleep(1)
    print('... Programmer!')

asyncio.run(main())

The above example works fine but is not actually utilizing the feature of asyncio. asyncio allows you to get performance benefit when you parallelize I/O blocking operations (like reading/writing to network).

Like in the following example, the asyncio executes another task when other pauses.

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

    
async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

Without Asyncio, it would have taken a total of 3 seconds to complete the above two tasks.

Use multiprocessing module for Asynchronous Function

Initialize the Pool object that with the number of processes and call the apply_async(func, args) on the pool object to asynchronously run the function func with arguments args.

from multiprocessing import Pool

def greet(x):
    return f'Hello {x}'


pool = Pool(processes=1)

# Evaluate "greet('Tom')" asynchronously calling callback when finished.
result = pool.apply_async(greet, ['Tom'], ) 

print(result.get())

Adarsh Kumar

I am an engineer by education and writer by passion. I started this blog to share my little programming wisdom with other programmers out there. Hope it helps you.

Leave a Reply