Python asyncio add_done_callback с async def

У меня есть 2 функции: первая, def_a, является асинхронной функцией, а вторая - def_b, которая является регулярной функцией и вызывается с результатом def_a в качестве обратного вызова с помощью add_done_callback функция.

Мой код выглядит следующим образом:

import asyncio

def def_b(result):
    next_number = result.result()
    # some work on the next_number
    print(next_number + 1)

async def def_a(number):
    await some_async_work(number)
    return number + 1

loop = asyncio.get_event_loop()
task = asyncio.ensure_future(def_a(1))
task.add_done_callback(def_b)
response = loop.run_until_complete(task)
loop.close()

И он отлично работает.

Проблема началась, когда вторая функция def_b стала асинхронной. Теперь это выглядит так:

async def def_b(result):
    next_number = result.result()
    # some asynchronous work on the next_number
    print(next_number + 1)

Но теперь я не могу предоставить его функции add_done_callback, потому что это не регулярная функция.

Мой вопрос: возможно ли это и как я могу предоставить def_b функции add_done_callback, если def_b является асинхронным?

Ответ 1

add_done_callback считается интерфейсом "низкого уровня". При работе с сопрограммами вы можете связать их разными способами, например:

import asyncio


async def my_callback(result):
    print("my_callback got:", result)
    return "My return value is ignored"


async def coro(number):
    await asyncio.sleep(number)
    return number + 1


async def add_success_callback(fut, callback):
    result = await fut
    await callback(result)
    return result


loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coro(1))
task = add_success_callback(task, my_callback)
response = loop.run_until_complete(task)
print("response:", response)
loop.close()

Имейте в виду, что add_done_callback все равно вызовет обратный вызов, если ваше будущее вызывает исключение (но вызов result.result() приведет к его повышению).

Ответ 2

Это работает только для одной будущей работы, если у вас есть несколько асинхронных заданий, они будут блокировать друг друга, лучше использовать asyncio.as_comleted() для итерации будущего списка:

import asyncio

async def __after_done_callback(future_result):
    # await for something...
    pass

async def __future_job(number):
    await some_async_work(number)
    return number + 1

loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(__future_job(x)) for x in range(100)]  # create 100 future jobs

for f in asyncio.as_completed(tasks):
    result = await f
    await __after_done_callback(result)

loop.close()