Python #4.1 | asyncio


Introduction

  • asyncio
    1. Coroutines
    2. Awaitable
    3. Creating Tasks


1. Coroutines

  • Declaration : Python functions defined with the def syntax are synchronous functions. However, to make a function asynchronous, you need to use the async keyword to the function definition. And we call this async funtion, Coroutine.

  • Run a coroutine : Simply calling a coroutine will not schedule it to be executed. asyncio provides the following mechanisms (multiple ways) to run a coroutine

    • ayncio.run() : run the top-level entry point

    • await : awaiting on a coroutine

    • ayncio.create_task() : run coroutines concurrently as asyncio Tasks

import asyncio
import time

# Define coroutine with async syntax 
async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

# takes 3secs to run
async def main():
    await say_after(1, 'hello')
    await say_after(2, 'world')

# takes 2secs to run
async def main(): 
    task1 = asyncio.create_task(say_after(1, 'hello'))
    task2 = asyncio.create_task(say_after(2, 'world'))
    await task1
    await task2

asyncio.run(main())


2. Awaitables

  • We say that an object is awaitable object if it can be used in an await expression

    • couroutines : are awaitables and therefore can be awaited from other coroutines

      • coroutine function: an async def function;

      • coroutine object: an object returned by calling a coroutine function.

    • Tasks : are used to schedule coroutines concurrently. When a coroutine is wrapped into a Task with functions like asyncio.create_task() the coroutine is automatically scheduled to run soon:

    • Futures : a special low-level awaitable object that represents an eventual result of an asynchronous operation.



3. Creating Tasks

  • asyncio.create_task(coro, *, name=None, context=None)
background_tasks = set()

for i in range(10):
    task = asyncio.create_task(some_coro(param=i))
    # Add task to the set. This creates a strong reference.
    background_tasks.add(task)
    # To prevent keeping references to finished tasks forever, 
    # make each task remove its own reference from the set after completion:
    task.add_done_callback(background_tasks.discard)


7. Running Tasks Concurrently

import asyncio

async def test(name, time):
    await asyncio.sleep(time)
    return "Done"

async def main():
    L = await asyncio.gather(test("A", 1), test("B", 2), test("C", 3))

asyncio.run(main()


References