Coroutine

Definition

Coroutines are a more generalized form of subroutines. Subroutines are entered at one point and exited at another point. Coroutines can be entered, exited, and resumed at many different points. They can be implemented with the async def statement. See also PEP 492.

Baby toy on top of generators

def coroutine(num):
    for i in range(1, num):
    
        from_user = yield i
        
        print(f'Send from user to generator {from_user}')


def from_users():
    c = coroutine(10)

    from_generator = c.send(None)
    print(f'From primed generator to base user {from_generator}')

    try:
        while True:
            from_generator = c.send("A")
            print(f'From generator to user {from_generator}')
    except StopIteration:
        print("reaching the end")

Expected output after calling from_user()

# prime the generator 
# yield 1
# yield from generator to user: 1
# send from user to generator: A
# yield from generator to user: 2
# send from user to generator: A
# yield from generator to user: 3
# ...
# reaching the end

The control of the program is getting passed back and forth between these 2 functions. This is pretty much a baby version of asyncio.

Basic async and await

definitions

  • coroutine - a function with async def

  • awaitable - any object with __await__ method (i.e. asyncio.Future, asyncio.Task)

  • task - a wrapper around coroutine and gets scheduled by the event loop independently

  • event loop - scheduler

async def f():
    print("hello")
async def g():
    print("world")

# execute f() then g()
result_f = await f()
result_g = await g() 

# execute f() and g() concurrently 
task_f = asyncio.create_task(f())
task_g = asyncio.create_task(g())

await asyncio.sleep(1)

result_f = await task_f
result_g = await task_g 

# asyncio.gather(*aws, return_exceptions=False) takes in one or more awaitable 
# if the awaitable is a coroutine, then schedule a task 
# wait for all tasks to finish then return values in order 
# https://docs.python.org/3/library/asyncio-task.html#asyncio.gather

result_f, result_g = await asyncio.gather(f(), g())

References

Last updated