Producir resultados de forma asíncrona

Un Futuro representa el resultado de un trabajo que no ha sido completado aún. El bucle de eventos puede seguir el estado de un objeto Future para indicar que está listo, permitiendo que una parte de una aplicación espere otra parte para terminar un trabajo.

Esperando un futuro

Un Future actúa como una co-rutina, por lo que cualquier técnica útil para esperar a una co-rutina también puede usarse para esperar que el futuro sea marcado como listo. Este ejemplo pasa el futuro al método run_until_complete() del bucle de eventos.

asyncio_future_event_loop.py
import asyncio


def mark_done(future, result):
    print('setting future result to {!r}'.format(result))
    future.set_result(result)


event_loop = asyncio.get_event_loop()
try:
    all_done = asyncio.Future()

    print('scheduling mark_done')
    event_loop.call_soon(mark_done, all_done, 'the result')

    print('entering event loop')
    result = event_loop.run_until_complete(all_done)
    print('returned result: {!r}'.format(result))
finally:
    print('closing event loop')
    event_loop.close()

print('future result: {!r}'.format(all_done.result()))

El estado del Future cambia a listo cuando se llama set_result(), y la instancia Future mantiene el resultado dado al método de recuperación posterior.

$ python3 asyncio_future_event_loop.py

scheduling mark_done
entering event loop
setting future result to 'the result'
returned result: 'the result'
closing event loop
future result: 'the result'

Un Future también se puede usar con la palabra clave await, como en este ejemplo.

asyncio_future_await.py
import asyncio


def mark_done(future, result):
    print('setting future result to {!r}'.format(result))
    future.set_result(result)


async def main(loop):
    all_done = asyncio.Future()

    print('scheduling mark_done')
    loop.call_soon(mark_done, all_done, 'the result')

    result = await all_done
    print('returned result: {!r}'.format(result))


event_loop = asyncio.get_event_loop()
try:
    event_loop.run_until_complete(main(event_loop))
finally:
    event_loop.close()

El resultado de Future es devuelto por await, por lo que es con frecuencia es posible tener el mismo código de trabajo con una co-rutina normal y una instancia Future.

$ python3 asyncio_future_await.py

scheduling mark_done
setting future result to 'the result'
returned result: 'the result'

Devoluciones de llamadas de Futures

Además de trabajar como una co-rutina, un Future puede invocar devoluciones de llamada cuando se completa. Las devoluciones de llamada se invocan en el orden en que están registrados

asyncio_future_callback.py
import asyncio
import functools


def callback(future, n):
    print('{}: future done: {}'.format(n, future.result()))


async def register_callbacks(all_done):
    print('registering callbacks on future')
    all_done.add_done_callback(functools.partial(callback, n=1))
    all_done.add_done_callback(functools.partial(callback, n=2))


async def main(all_done):
    await register_callbacks(all_done)
    print('setting result of future')
    all_done.set_result('the result')


event_loop = asyncio.get_event_loop()
try:
    all_done = asyncio.Future()
    event_loop.run_until_complete(main(all_done))
finally:
    event_loop.close()

La devolución de llamada debe esperar un argumento, la instancia Future. Para pasar argumentos adicionales a las devoluciones de llamada, use functools.partial() para crear un envoltorio.

$ python3 asyncio_future_callback.py

registering callbacks on future
setting result of future
1: future done: the result
2: future done: the result