Skip to main content

Command Palette

Search for a command to run...

Coroutines

Updated
2 min read
J

Python Django Developer | Django Rest Framework, AWS | Melbourne-based | Available for Immediate Start

Normal functions are like single-focus tasks.

Coroutines allow for multitasking by alternating between different activities.

async def coroutine_multiply_be_two(number: int) -> int: 
    return nubmer * 2

def multiply_by_two(number: int) -> int: 
    return number * 2

multiply_by_two runs immediately and returns the result.

coroutine_multiply_by_two, the code inside the coroutine doesn’t execute right away. Instead, it returns a coroutine object, which means the coroutine has been defined but hasn’t been run yet.

Essential Understanding of Coroutines

They don’t execute upon being called. Instead, they produce a coroutine object that can be scheduled and executed later by an event loop.

Event Loop

The event loop ensures that your program remains responsive by handling these operations without blocking the main execution flow.

By coordinating the execution of coroutines, the event loop facilitates concurrent task processing.

Tools

To execute coroutines, we use specific tools provided by Python’s asyncio library. The asyncio.run() function starts the event loop and run main coroutine, while keyword await allows us to pause and resume coroutines.

Running

import asyncio

async def fetch_data(data_id: int) -> str: 
    print(f'Fetching data for ID {data_id}')
    await asyncio.sleep(2) # Simulates a delay like a network request
    print(f'Data fetched for ID {data_id}')
    return f'Data {data_id}'

async def compute_result(value: int) -> int: 
    await asyncio.sleep(1) # Simulates a delay like a computation  
    return value * 2

async def process_data() -> None: 
    data = await fetch_data(1)  # 2s
    result = await compute_result(5) # 1s
    print(f'Result: {result}')
    print(f'Processed Data: {data}')

asyncio.run(process_data())

asyncio.run(process_data()) is the starting point.

await keyword is used, the coroutine pauses, allowing other operations to run.

Utilizing

Inefficient

import asyncio

async def fetch_data(data_id: int) -> None: 
    print(f'Fetching data for ID {data_id}')
    await asyncio.sleep(3) # Simulates waiting for a response from a server
    print(f'Finished fetching data for ID {data_id}')

async def main() -> None: 
    await fetch_data(1)
    await fetch_data(2)
    await fetch_data(3)

asyncio.run(main())

Each fetch_data waits for the previous one to complete.

This sequential execution is straightforward but can be inefficient for tasks that can be performed concurrently.

Efficient

import asyncio

async def fetch_data(data_id: int) -> None: 
    print(f'Fetching data for ID {data_id}')
    await asyncio.sleep(3) # Simulates waiting for a response from a server
    print(f'Finished fetching data for ID {data_id}')

async def main() -> None: 
    # Create tasks for concurrent execution
    task1 = asyncio.create_task(fetch_data(1))
    task2 = asyncio.create_task(fetch_data(2))
    task3 = asyncio.create_task(fetch_data(3))

    # Await all tasks
    await task1
    await task2
    await task3

 asyncio.run(main())

Each fetch_data starts running as soon as it is created as a task. All run concurrently.

Reference: https://medium.com/python-features/understanding-coroutines-tasks-in-depth-in-python-af2a4c0e1073