Corrutinas y Tareas¶
Esta sección describe las API de asyncio de alto nivel para trabajar con corrutinas y tareas.
Corrutinas¶
Coroutines declarado con la sintaxis async/await es la forma preferida de escribir aplicaciones asyncio. Por ejemplo, el siguiente fragmento de código (requiere Python 3.7+) imprime «hola», espera 1 segundo y, a continuación, imprime «mundo»:
>>> import asyncio
>>> async def main():
... print('hello')
... await asyncio.sleep(1)
... print('world')
>>> asyncio.run(main())
hello
world
Tenga en cuenta que simplemente llamando a una corrutina no programará para que se ejecute:
>>> main()
<coroutine object main at 0x1053bb7c8>
Para ejecutar realmente una corrutina, asyncio proporciona tres mecanismos principales:
La función
asyncio.run()
para ejecutar la función de punto de entrada de nivel superior «main()» (consulte el ejemplo anterior.)Esperando en una corrutina. El siguiente fragmento de código imprimirá «hola» después de esperar 1 segundo y luego imprimirá «mundo» después de esperar otros 2 segundos:
import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): print(f"started at {time.strftime('%X')}") await say_after(1, 'hello') await say_after(2, 'world') print(f"finished at {time.strftime('%X')}") asyncio.run(main())
Salida esperada:
started at 17:13:52 hello world finished at 17:13:55
La función
asyncio.create_task()
para ejecutar corrutinas concurrentemente como asyncioTasks
.Modifiquemos el ejemplo anterior y ejecutemos dos corrutinas
say_after
concurrentemente:async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # Wait until both tasks are completed (should take # around 2 seconds.) await task1 await task2 print(f"finished at {time.strftime('%X')}")
Tenga en cuenta que la salida esperada ahora muestra que el fragmento de código se ejecuta 1 segundo más rápido que antes:
started at 17:14:32 hello world finished at 17:14:34
Esperables¶
Decimos que un objeto es un objeto esperable si se puede utilizar en una expresión await
. Muchas API de asyncio están diseñadas para aceptar los valores esperables.
Hay tres tipos principales de objetos esperables: corrutinas, Tareas y Futuros.
Corrutinas
Las corrutinas de Python son esperables y por lo tanto se pueden esperar de otras corrutinas:
import asyncio
async def nested():
return 42
async def main():
# Nothing happens if we just call "nested()".
# A coroutine object is created but not awaited,
# so it *won't run at all*.
nested()
# Let's do it differently now and await it:
print(await nested()) # will print "42".
asyncio.run(main())
Importante
En esta documentación se puede utilizar el término «corrutina» para dos conceptos estrechamente relacionados:
una función corrutina: una función
async def
;un objeto corrutina: un objeto retornado llamando a una función corrutina.
asyncio también es compatible con corrutinas heredadas generator-based.
Tareas
Las tareas se utilizan para programar corrutinas concurrentemente.
Cuando una corrutina se envuelve en una Tarea con funciones como asyncio.create_task()
la corrutina se programa automáticamente para ejecutarse pronto:
import asyncio
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())
Futuros
Un Future
es un objeto esperable especial de bajo-nivel que representa un resultado eventual de una operación asíncrona.
Cuando un objeto Future es esperado significa que la corrutina esperará hasta que el Future se resuelva en algún otro lugar.
Los objetos Future de asyncio son necesarios para permitir que el código basado en retro llamada se use con async/await.
Normalmente , no es necesario crear objetos Future en el código de nivel de aplicación.
Los objetos Future, a veces expuestos por bibliotecas y algunas API de asyncio, pueden ser esperados:
async def main():
await function_that_returns_a_future_object()
# this is also valid:
await asyncio.gather(
function_that_returns_a_future_object(),
some_python_coroutine()
)
Un buen ejemplo de una función de bajo nivel que retorna un objeto Future es loop.run_in_executor()
.
Ejecutando un programa asyncio¶
-
asyncio.
run
(coro, *, debug=False)¶ Ejecuta la coroutine coro y retornando el resultado.
Esta función ejecuta la corrutina pasada, encargándose de administrar el bucle de eventos asyncio y finalizar los generadores asíncronos.
Esta función no se puede llamar cuando otro bucle de eventos asyncio se está ejecutando en el mismo hilo.
Si debug es
True
, el bucle de eventos se ejecutará en modo debug.Esta función siempre crea un nuevo ciclo de eventos y lo cierra al final. Debe usarse como un punto de entrada principal para los programas asyncio, e idealmente solo debe llamarse una vez.
Ejemplo:
async def main(): await asyncio.sleep(1) print('hello') asyncio.run(main())
Nuevo en la versión 3.7.
Nota
El código fuente para
asyncio.run()
se puede encontrar en Lib/asyncio/runners.py.
Creando Tareas¶
-
asyncio.
create_task
(coro, *, name=None)¶ Envuelve una coroutine coro en una
Task
y programa su ejecución. Retorna el objeto Tarea.Si name no es
None
, se establece como el nombre de la tarea medianteTask.set_name()
.La tarea se ejecuta en el bucle retornado por
get_running_loop()
,RuntimeError
se genera si no hay ningún bucle en ejecución en el subproceso actual.Esta función se ha añadido en Python 3.7. Antes de Python 3.7, la función de bajo nivel
asyncio.ensure_future()
se puede utilizar en su lugar:async def coro(): ... # In Python 3.7+ task = asyncio.create_task(coro()) ... # This works in all Python versions but is less readable task = asyncio.ensure_future(coro()) ...
Nuevo en la versión 3.7.
Distinto en la versión 3.8: Se ha añadido el parámetro
name
.
Durmiendo¶
-
coroutine
asyncio.
sleep
(delay, result=None, *, loop=None)¶ Bloquea por delay segundos.
Si se proporciona result, se retorna al autor de la llamada cuando se completa la corrutina.
sleep()
siempre suspende la tarea actual, permitiendo que se ejecuten otras tareas.Deprecated since version 3.8, will be removed in version 3.10: El parámetro loop.
Ejemplo de una rutina que muestra la fecha actual cada segundo durante 5 segundos:
import asyncio import datetime async def display_date(): loop = asyncio.get_running_loop() end_time = loop.time() + 5.0 while True: print(datetime.datetime.now()) if (loop.time() + 1.0) >= end_time: break await asyncio.sleep(1) asyncio.run(display_date())
Ejecutando Tareas Concurrentemente¶
-
awaitable
asyncio.
gather
(*aws, loop=None, return_exceptions=False)¶ Ejecute objetos esperables en la secuencia aws de forma concurrently.
Si cualquier esperable en aws es una corrutina, se programa automáticamente como una Tarea.
Si todos los esperables se completan correctamente, el resultado es una lista agregada de valores retornados. El orden de los valores de resultado corresponde al orden de esperables en aws.
Si return_exceptions es
False
(valor predeterminado), la primera excepción provocada se propaga inmediatamente a la tarea que espera engather()
. Otros esperables en la secuencia aws no se cancelarán y continuarán ejecutándose.Si return_exceptions es
True
, las excepciones se tratan igual que los resultados correctos y se agregan en la lista de resultados.Si
gather()
es cancelado, todos los esperables enviados (que aún no se han completado) también se cancelan.Si alguna Tarea o Futuro de la secuencia aws se cancela, se trata como si se lanzara
CancelledError
– la llamadagather()
no se cancela en este caso. Esto es para evitar la cancelación de una Tarea/Futuro enviada para hacer que otras Tareas/Futuros sean canceladas.Deprecated since version 3.8, will be removed in version 3.10: El parámetro loop.
Ejemplo:
import asyncio async def factorial(name, number): f = 1 for i in range(2, number + 1): print(f"Task {name}: Compute factorial({i})...") await asyncio.sleep(1) f *= i print(f"Task {name}: factorial({number}) = {f}") async def main(): # Schedule three calls *concurrently*: await asyncio.gather( factorial("A", 2), factorial("B", 3), factorial("C", 4), ) asyncio.run(main()) # Expected output: # # Task A: Compute factorial(2)... # Task B: Compute factorial(2)... # Task C: Compute factorial(2)... # Task A: factorial(2) = 2 # Task B: Compute factorial(3)... # Task C: Compute factorial(3)... # Task B: factorial(3) = 6 # Task C: Compute factorial(4)... # Task C: factorial(4) = 24
Nota
If return_exceptions is False, cancelling gather() after it has been marked done won’t cancel any submitted awaitables. For instance, gather can be marked done after propagating an exception to the caller, therefore, calling
gather.cancel()
after catching an exception (raised by one of the awaitables) from gather won’t cancel any other awaitables.Distinto en la versión 3.7: Si se cancela el propio gather, la cancelación se propaga independientemente de return_exceptions.
Protección contra Cancelación¶
-
awaitable
asyncio.
shield
(aw, *, loop=None)¶ Protege un objeto esperable de ser
cancelado
.Si aw es una corrutina, se programa automáticamente como una Tarea.
La declaración:
res = await shield(something())
es equivalente a:
res = await something()
excepto que si la corrutina que lo contiene se cancela, la tarea que se ejecuta en
something()
no se cancela. Desde el punto de vista desomething()
, la cancelación no ocurrió. Aunque su invocador siga cancelado, por lo que la expresión «await» sigue generando unCancelledError
.Si
something()
se cancela por otros medios (es decir, desde dentro de sí mismo) eso también cancelaríashield()
.Si se desea ignorar por completo la cancelación (no se recomienda) la función
shield()
debe combinarse con una cláusula try/except, como se indica a continuación:try: res = await shield(something()) except CancelledError: res = None
Deprecated since version 3.8, will be removed in version 3.10: El parámetro loop.
Tiempo agotado¶
-
coroutine
asyncio.
wait_for
(aw, timeout, *, loop=None)¶ Espere a que el aw esperable se complete con un tiempo agotado.
Si aw es una corrutina, se programa automáticamente como una Tarea.
timeout puede ser
None
o punto flotante o un número entero de segundos a esperar. Si timeout esNone
, se bloquea hasta que futuro se complete.Si se produce un agotamiento de tiempo, cancela la tarea y genera
asyncio.TimeoutError
.Para evitar la
cancelación
de la tarea , envuélvala enshield()
.La función esperará hasta que el futuro este cancelado, por lo que el tiempo de espera total puede superar el timeout.
Si se cancela la espera, el futuro aw también se cancela.
Deprecated since version 3.8, will be removed in version 3.10: El parámetro loop.
Ejemplo:
async def eternity(): # Sleep for one hour await asyncio.sleep(3600) print('yay!') async def main(): # Wait for at most 1 second try: await asyncio.wait_for(eternity(), timeout=1.0) except asyncio.TimeoutError: print('timeout!') asyncio.run(main()) # Expected output: # # timeout!
Distinto en la versión 3.7: Cuando aw se cancela debido a un agotamiento de tiempo,
wait_for
espera a que se cancele aw. Anteriormente, se lanzó inmediatamenteasyncio.TimeoutError
.
Esperando Primitivas¶
-
coroutine
asyncio.
wait
(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)¶ Run awaitable objects in the aws iterable concurrently and block until the condition specified by return_when.
Retorna dos conjuntos de Tareas/Futuros:
(done, pending)
.Uso:
done, pending = await asyncio.wait(aws)
timeout (un punto flotante o int), si se especifica, se puede utilizar para controlar el número máximo de segundos que hay que esperar antes de retornar.
Tenga en cuenta que esta función no lanza
asyncio.TimeoutError
. Los Futuros o Tareas que no terminan cuando se agota el tiempo simplemente se retornan en el segundo conjunto.return_when indica cuándo debe retornar esta función. Debe ser una de las siguientes constantes:
Constante
Descripción
FIRST_COMPLETED
La función retornará cuando cualquier Futuro termine o se cancele.
FIRST_EXCEPTION
La función retornará cuando cualquier Futuro finalice lanzando una excepción. Si ningún Futuro lanza una excepción, entonces es equivalente a
ALL_COMPLETED
.ALL_COMPLETED
La función retornará cuando todos los Futuros terminen o se cancelen.
A diferencia de
wait_for()
,wait()
no cancela los Futuros cuando se produce un agotamiento de tiempo.Obsoleto desde la versión 3.8: Si cualquier aguardable en aws es una corrutina, se programa automáticamente como una Tarea. El paso de objetos corrutinas a
wait()
directamente está en desuso, ya que conduce a comportamiento confuso.Deprecated since version 3.8, will be removed in version 3.10: El parámetro loop.
Nota
wait()
programa las corrutinas como Tareas automáticamente y posteriormente retorna los objetos Tarea creados implícitamente en conjuntos(done, pending)
. Por lo tanto, el código siguiente no funcionará como se esperaba:async def foo(): return 42 coro = foo() done, pending = await asyncio.wait({coro}) if coro in done: # This branch will never be run!
Aquí es cómo se puede arreglar el fragmento de código anterior:
async def foo(): return 42 task = asyncio.create_task(foo()) done, pending = await asyncio.wait({task}) if task in done: # Everything will work as expected now.
Obsoleto desde la versión 3.8: El paso de objetos corrutina a
wait()
directamente está en desuso.
-
asyncio.
as_completed
(aws, *, loop=None, timeout=None)¶ Run awaitable objects in the aws iterable concurrently. Return an iterator of coroutines. Each coroutine returned can be awaited to get the earliest next result from the iterable of the remaining awaitables.
Lanza
asyncio.TimeoutError
si el agotamiento de tiempo ocurre antes que todos los Futuros terminen.Deprecated since version 3.8, will be removed in version 3.10: El parámetro loop.
Ejemplo:
for coro in as_completed(aws): earliest_result = await coro # ...
Planificación Desde Otros Hilos¶
-
asyncio.
run_coroutine_threadsafe
(coro, loop)¶ Envía una corrutina al bucle de eventos especificado. Seguro para Hilos.
Retorna
concurrent.futures.Future
para esperar el resultado de otro hilo del SO (Sistema Operativo).Esta función está pensada para llamarse desde un hilo del SO diferente al que se ejecuta el bucle de eventos. Ejemplo:
# Create a coroutine coro = asyncio.sleep(1, result=3) # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(coro, loop) # Wait for the result with an optional timeout argument assert future.result(timeout) == 3
Si se lanza una excepción en la corrutina, el Futuro retornado será notificado. También se puede utilizar para cancelar la tarea en el bucle de eventos:
try: result = future.result(timeout) except asyncio.TimeoutError: print('The coroutine took too long, cancelling the task...') future.cancel() except Exception as exc: print(f'The coroutine raised an exception: {exc!r}') else: print(f'The coroutine returned: {result!r}')
Consulte la sección de la documentación Concurrencia y multi hilos.
A diferencia de otras funciones asyncio, esta función requiere que el argumento loop se pase explícitamente.
Nuevo en la versión 3.5.1.
Introspección¶
-
asyncio.
current_task
(loop=None)¶ Retorna la instancia
Task
actualmente en ejecución oNone
si no se está ejecutando ninguna tarea.Si loop es
None
get_running_loop()
se utiliza para obtener el bucle actual.Nuevo en la versión 3.7.
-
asyncio.
all_tasks
(loop=None)¶ Retorna un conjunto de objetos
Task
que se ejecutan por el bucle.Si loop es
None
,get_running_loop()
se utiliza para obtener el bucle actual.Nuevo en la versión 3.7.
Objeto Task¶
-
class
asyncio.
Task
(coro, *, loop=None, name=None)¶ Un objeto
similar a Futuro
que ejecuta Python coroutine. No es seguro hilos.Las tareas se utilizan para ejecutar corrutinas en bucles de eventos. Si una corrutina aguarda en un Futuro, la Tarea suspende la ejecución de la corrutina y espera la finalización del Futuro. Cuando el Futuro termina, se reanuda la ejecución de la corrutina envuelta.
Los bucles de eventos usan la programación cooperativa: un bucle de eventos ejecuta una tarea a la vez. Mientras una Tarea espera para la finalización de un Futuro, el bucle de eventos ejecuta otras tareas, retorno de llamada o realiza operaciones de E/S.
Utilice la función de alto nivel
asyncio.create_task()
para crear Tareas, o las funciones de bajo nivelloop.create_task()
oensure_future()
. Se desaconseja la creación de instancias manuales de Tareas.Para cancelar una Tarea en ejecución, utilice el método
cancel()
. Llamarlo hará que la tarea lance una excepciónCancelledError
en la corrutina contenida. Si una corrutina está esperando en un objeto Futuro durante la cancelación, se cancelará el objeto Futuro.cancelled()
se puede utilizar para comprobar si la Tarea fue cancelada. El método devuelveTrue
si la corrutina contenida no suprimió la excepciónCancelledError
y se canceló realmente.asyncio.Task
hereda deFuture
todas sus API exceptoFuture.set_result()
yFuture.set_exception()
.Las tareas admiten el módulo
contextvars
. Cuando se crea una Tarea, copia el contexto actual y, posteriormente, ejecuta su corrutina en el contexto copiado.Distinto en la versión 3.7: Agregado soporte para el módulo
contextvars
.Distinto en la versión 3.8: Se ha añadido el parámetro
name
.Deprecated since version 3.8, will be removed in version 3.10: El parámetro loop.
-
cancel
()¶ Solicita que se cancele la Tarea.
Esto hace que una excepción
CancelledError
sea lanzada a la corrutina contenida en el próximo ciclo del bucle de eventos.La corrutina entonces tiene la oportunidad de limpiar o incluso denegar la solicitud suprimiendo la excepción con un bloque
try
… …``except CancelledError`` …finally
. Por lo tanto, a diferencia deFuture.cancel()
,Task.cancel()
no garantiza que la tarea será cancelada, aunque suprimir la cancelación por completo no es común y se desalienta activamente.En el ejemplo siguiente se muestra cómo las corrutinas pueden interceptar la solicitud de cancelación:
async def cancel_me(): print('cancel_me(): before sleep') try: # Wait for 1 hour await asyncio.sleep(3600) except asyncio.CancelledError: print('cancel_me(): cancel sleep') raise finally: print('cancel_me(): after sleep') async def main(): # Create a "cancel_me" Task task = asyncio.create_task(cancel_me()) # Wait for 1 second await asyncio.sleep(1) task.cancel() try: await task except asyncio.CancelledError: print("main(): cancel_me is cancelled now") asyncio.run(main()) # Expected output: # # cancel_me(): before sleep # cancel_me(): cancel sleep # cancel_me(): after sleep # main(): cancel_me is cancelled now
-
cancelled
()¶ Retorna
True
si la Tarea se cancela.La tarea se cancela cuando se solicitó la cancelación con
cancel()
y la corrutina contenida propagó la excepciónCancelledError
que se le ha lanzado.
-
done
()¶ Retorna
True
si la Tarea está finalizada.Una tarea está finalizada cuando la corrutina contenida retornó un valor, lanzó una excepción, o se canceló la Tarea.
-
result
()¶ Retorna el resultado de la Tarea.
Si la tarea está terminada, se devuelve el resultado de la corrutina contenida (o si la corrutina lanzó una excepción, esa excepción se vuelve a relanzar.)
Si la Tarea ha sido cancelada, este método lanza una excepción
CancelledError
.Si el resultado de la Tarea aún no está disponible, este método lanza una excepción
InvalidStateError
.
-
exception
()¶ Retorna la excepción de la Tarea.
Si la corrutina contenida lanzó una excepción, esa excepción es retornada. Si la corrutina contenida retorna normalmente, este método retorna
None
.Si la Tarea ha sido cancelada, este método lanza una excepción
CancelledError
.Si la Tarea aún no está terminada, este método lanza una excepción
InvalidStateError
.
-
add_done_callback
(callback, *, context=None)¶ Agrega una retro llamada que se ejecutará cuando la Tarea esté terminada.
Este método solo se debe usar en código basado en retrollamada de bajo nivel.
Consulte la documentación de
Future.add_done_callback()
para obtener más detalles.
-
remove_done_callback
(callback)¶ Remueve la retrollamada de la lista de retrollamadas.
Este método solo se debe usar en código basado en retrollamada de bajo nivel.
Consulte la documentación de
Future.remove_done_callback()
para obtener más detalles.
-
get_stack
(*, limit=None)¶ Retorna la lista de marcos de pila para esta tarea.
Si la corrutina contenida no se termina, esto retorna la pila donde se suspende. Si la corrutina se ha completado correctamente o se ha cancelado, retorna una lista vacía. Si la corrutina terminó por una excepción, esto retorna la lista de marcos de seguimiento.
Los marcos siempre se ordenan de más antiguo a más nuevo.
Solo se retorna un marco de pila para una corrutina suspendida.
El argumento opcional limit establece el número máximo de marcos que se retornarán; de forma predeterminada se retornan todos los marcos disponibles. El orden de la lista devuelta varía en función de si se retorna una pila o un traceback: se devuelven los marcos más recientes de una pila, pero se devuelven los marcos más antiguos de un traceback. (Esto coincide con el comportamiento del módulo traceback.)ss
-
print_stack
(*, limit=None, file=None)¶ Imprime la pila o el seguimiento de esta tarea.
Esto produce una salida similar a la del módulo traceback para los marcos recuperados por
get_stack()
.El argumento limit se pasa directamente a
get_stack()
.El argumento file es un flujo de E/S en el que se escribe la salida; por defecto, la salida se escribe en
sys.stderr
.
-
get_name
()¶ Retorna el nombre de la Tarea.
Si no se ha asignado explícitamente ningún nombre a la Tarea, la implementación de Tarea asyncio predeterminada genera un nombre predeterminado durante la creación de instancias.
Nuevo en la versión 3.8.
-
set_name
(value)¶ Establece el nombre de la Tarea.
El argumento value puede ser cualquier objeto, que luego se convierte en una cadena.
En la implementación de Task predeterminada, el nombre será visible en la salida
repr()
de un objeto de tarea.Nuevo en la versión 3.8.
-
classmethod
all_tasks
(loop=None)¶ Retorna un conjunto de todas las tareas para un bucle de eventos.
De forma predeterminada, se retornan todas las tareas del bucle de eventos actual. Si loop es
None
, la funciónget_event_loop()
se utiliza para obtener el bucle actual.Deprecated since version 3.7, will be removed in version 3.9: No llame a esto como un método de tarea. Utilice la función
asyncio.all_tasks()
en su lugar.
-
classmethod
current_task
(loop=None)¶ Retorna la tarea que se está ejecutando actualmente o
None
.Si bucle es
None
, la funciónget_event_loop()
se utiliza para obtener el bucle actual.Deprecated since version 3.7, will be removed in version 3.9: No llame a esto como un método de tarea. Utilice la función
asyncio.current_task()
en su lugar.
-
Corrutinas basadas en generadores¶
Nota
La compatibilidad con corrutinas basadas en generadores está en desuso y está programada para su eliminación en Python 3.10.
Las corrutinas basadas en generadores son anteriores a la sintaxis async/await. Son generadores de Python que utilizan expresiones yield from
para esperar en Futuros y otras corrutinas.
Las corrutinas basadas en generadores deben estar decoradas con @asyncio.coroutine
, aunque esto no se aplica.
-
@
asyncio.
coroutine
¶ Decorador para marcar corrutinas basadas en generadores.
Este decorador permite que las corrutinas basadas en generadores de versiones anteriores (legacy) sean compatibles con el código async/await:
@asyncio.coroutine def old_style_coroutine(): yield from asyncio.sleep(1) async def main(): await old_style_coroutine()
Este decorador no debe utilizarse para corrutinas
async def
.Deprecated since version 3.8, will be removed in version 3.10: Usar
async def
en su lugar.
-
asyncio.
iscoroutine
(obj)¶ Retorna
True
si obj es un coroutine object.Este método es diferente de
inspect.iscoroutine()
porque retornaTrue
para corrutinas basadas en generadores.
-
asyncio.
iscoroutinefunction
(func)¶ Retorna
True
si func es una coroutine function.Este método es diferente de
inspect.iscoroutinefunction()
porque retornaTrue
para funciones de corrutinas basadas en generadores decoradas con@coroutine
.