web-dev-qa-db-fra.com

python asyncio add_done_callback avec async def

J'ai 2 fonctions: la première, def_a, est une fonction asynchrone et la seconde est def_b qui est une fonction régulière et appelée avec le résultat de def_a comme rappel avec le add_done_callback fonction.

Mon code ressemble à ceci:

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()

Et ça marche parfaitement.

Le problème a commencé lorsque la deuxième fonction, def_b, est devenu asynchrone. Maintenant, cela ressemble à ceci:

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

Mais maintenant, je ne peux pas le fournir au add_done_callback fonction, car ce n'est pas une fonction régulière.

Ma question est: est-ce possible et comment puis-je fournir def_b à la add_done_callback fonction si def_b est asynchrone?

13
Yuval Pruss

add_done_callback Est considéré comme une interface "de bas niveau". Lorsque vous travaillez avec des coroutines, vous pouvez les enchaîner de plusieurs façons, par exemple:

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()

Gardez à l'esprit que add_done_callback Appellera toujours le rappel si votre futur déclenche une exception (mais appeler result.result() la déclenchera).

20
Udi

Cela ne fonctionne que pour un futur travail, si vous avez plusieurs travaux asynchrones, ils se bloqueront mutuellement, une meilleure façon est d'utiliser asyncio.as_comleted () pour itérer la future liste:

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()
1
user3593261