Corrutinas y tareas
*******************

Esta sección describe las API de asyncio de alto nivel para trabajar
con corrutinas y tareas.

* Corrutinas

* Esperables

* Creando Tareas

* Task Cancellation

* Task Groups

* Durmiendo

* Ejecutando tareas concurrentemente

* Protección contra cancelación

* Tiempo agotado

* Esperando primitivas

* Ejecutando en hilos

* Planificación desde otros hilos

* Introspección

* Objeto Task


Corrutinas
==========

**Source code:** Lib/asyncio/coroutines.py

======================================================================

*Coroutines* declared with the async/await syntax is the preferred way
of writing asyncio applications.  For example, the following snippet
of code prints "hello", waits 1 second, and then prints "world":

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

To actually run a coroutine, asyncio provides the following
mechanisms:

* 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 asyncio "Tasks".

  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

* The "asyncio.TaskGroup" class provides a more modern alternative to
  "create_task()". Using this API, the last example becomes:

     async def main():
         async with asyncio.TaskGroup() as tg:
             task1 = tg.create_task(
                 say_after(1, 'hello'))

             task2 = tg.create_task(
                 say_after(2, 'world'))

             print(f"started at {time.strftime('%X')}")

         # The await is implicit when the context manager exits.

         print(f"finished at {time.strftime('%X')}")

  The timing and output should be the same as for the previous
  version.

  Nuevo en la versión 3.11: "asyncio.TaskGroup".


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 **Futures**.

-[ 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*.

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

-[ Futures ]-

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


Creando Tareas
==============

**Source code:** Lib/asyncio/tasks.py

======================================================================

asyncio.create_task(coro, *, name=None, context=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
   mediante "Task.set_name()".

   An optional keyword-only *context* argument allows specifying a
   custom "contextvars.Context" for the *coro* to run in. The current
   context copy is created when no *context* is provided.

   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.

   Nota:

     "asyncio.TaskGroup.create_task()" is a newer alternative that
     allows for convenient waiting for a group of related tasks.

   Importante:

     Save a reference to the result of this function, to avoid a task
     disappearing mid-execution. The event loop only keeps weak
     references to tasks. A task that isn't referenced elsewhere may
     get garbage collected at any time, even before it's done. For
     reliable "fire-and-forget" background tasks, gather them in a
     collection:

        background_tasks = set()

        for i in range(10):
            task = asyncio.create_task(some_coro(param=i))

            # Add task to the set. This creates a strong reference.
            background_tasks.add(task)

            # To prevent keeping references to finished tasks forever,
            # make each task remove its own reference from the set after
            # completion:
            task.add_done_callback(background_tasks.discard)

   Nuevo en la versión 3.7.

   Distinto en la versión 3.8: Added the *name* parameter.

   Distinto en la versión 3.11: Added the *context* parameter.


Task Cancellation
=================

Tasks can easily and safely be cancelled. When a task is cancelled,
"asyncio.CancelledError" will be raised in the task at the next
opportunity.

It is recommended that coroutines use "try/finally" blocks to robustly
perform clean-up logic. In case "asyncio.CancelledError" is explicitly
caught, it should generally be propagated when clean-up is complete.
"asyncio.CancelledError" directly subclasses "BaseException" so most
code will not need to be aware of it.

The asyncio components that enable structured concurrency, like
"asyncio.TaskGroup" and "asyncio.timeout()", are implemented using
cancellation internally and might misbehave if a coroutine swallows
"asyncio.CancelledError". Similarly, user code should not generally
call "uncancel". However, in cases when suppressing
"asyncio.CancelledError" is truly desired, it is necessary to also
call "uncancel()" to completely remove the cancellation state.


Task Groups
===========

Task groups combine a task creation API with a convenient and reliable
way to wait for all tasks in the group to finish.

class asyncio.TaskGroup

   An asynchronous context manager holding a group of tasks. Tasks can
   be added to the group using "create_task()". All tasks are awaited
   when the context manager exits.

   Nuevo en la versión 3.11.

   create_task(coro, *, name=None, context=None)

      Create a task in this task group. The signature matches that of
      "asyncio.create_task()".

Ejemplo:

   async def main():
       async with asyncio.TaskGroup() as tg:
           task1 = tg.create_task(some_coro(...))
           task2 = tg.create_task(another_coro(...))
       print("Both tasks have completed now.")

The "async with" statement will wait for all tasks in the group to
finish. While waiting, new tasks may still be added to the group (for
example, by passing "tg" into one of the coroutines and calling
"tg.create_task()" in that coroutine). Once the last task has finished
and the "async with" block is exited, no new tasks may be added to the
group.

The first time any of the tasks belonging to the group fails with an
exception other than "asyncio.CancelledError", the remaining tasks in
the group are cancelled. No further tasks can then be added to the
group. At this point, if the body of the "async with" statement is
still active (i.e., "__aexit__()" hasn't been called yet), the task
directly containing the "async with" statement is also cancelled. The
resulting "asyncio.CancelledError" will interrupt an "await", but it
will not bubble out of the containing "async with" statement.

Once all tasks have finished, if any tasks have failed with an
exception other than "asyncio.CancelledError", those exceptions are
combined in an "ExceptionGroup" or "BaseExceptionGroup" (as
appropriate; see their documentation) which is then raised.

Two base exceptions are treated specially: If any task fails with
"KeyboardInterrupt" or "SystemExit", the task group still cancels the
remaining tasks and waits for them, but then the initial
"KeyboardInterrupt" or "SystemExit" is re-raised instead of
"ExceptionGroup" or "BaseExceptionGroup".

If the body of the "async with" statement exits with an exception (so
"__aexit__()" is called with an exception set), this is treated the
same as if one of the tasks failed: the remaining tasks are cancelled
and then waited for, and non-cancellation exceptions are grouped into
an exception group and raised. The exception passed into
"__aexit__()", unless it is "asyncio.CancelledError", is also included
in the exception group. The same special case is made for
"KeyboardInterrupt" and "SystemExit" as in the previous paragraph.


Durmiendo
=========

coroutine asyncio.sleep(delay, result=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.

   Establecer el retraso en 0 proporciona una ruta optimizada para
   permitir que se ejecuten otras tareas. Esto puede ser utilizado por
   funciones de ejecución prolongada para evitar bloquear el bucle de
   eventos durante toda la duración de la llamada a la función.

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

   Distinto en la versión 3.10: Removed the *loop* parameter.


Ejecutando tareas concurrentemente
==================================

awaitable asyncio.gather(*aws, 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 en "gather()". 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 Future de la secuencia *aws* se *cancela*, se
   trata como si se lanzara "CancelledError" -- la llamada "gather()"
   **no** se cancela en este caso. Esto es para evitar la cancelación
   de una Tarea/Future enviada para hacer que otras Tareas/Futures
   sean canceladas.

   Nota:

     A more modern way to create and run tasks concurrently and wait
     for their completion is "asyncio.TaskGroup".

   Ejemplo:

      import asyncio

      async def factorial(name, number):
          f = 1
          for i in range(2, number + 1):
              print(f"Task {name}: Compute factorial({number}), currently i={i}...")
              await asyncio.sleep(1)
              f *= i
          print(f"Task {name}: factorial({number}) = {f}")
          return f

      async def main():
          # Schedule three calls *concurrently*:
          L = await asyncio.gather(
              factorial("A", 2),
              factorial("B", 3),
              factorial("C", 4),
          )
          print(L)

      asyncio.run(main())

      # Expected output:
      #
      #     Task A: Compute factorial(2), currently i=2...
      #     Task B: Compute factorial(3), currently i=2...
      #     Task C: Compute factorial(4), currently i=2...
      #     Task A: factorial(2) = 2
      #     Task B: Compute factorial(3), currently i=3...
      #     Task C: Compute factorial(4), currently i=3...
      #     Task B: factorial(3) = 6
      #     Task C: Compute factorial(4), currently i=4...
      #     Task C: factorial(4) = 24
      #     [2, 6, 24]

   Nota:

     Si *return_exceptions* es False, cancelar gather() después de que
     se haya marcado como hecho no cancelará ninguna espera enviada.
     Por ejemplo, la recopilación se puede marcar como hecha después
     de propagar una excepción a la persona que llama, por lo tanto,
     llamar a "gather.cancel()" después de detectar una excepción
     (generada por uno de los elementos pendientes) de recopilación no
     cancelará ningún otro elemento pendiente.

   Distinto en la versión 3.7: Si se cancela el propio *gather*, la
   cancelación se propaga independientemente de *return_exceptions*.

   Distinto en la versión 3.10: Removed the *loop* parameter.

   Obsoleto desde la versión 3.10: Se emite una advertencia de
   obsolescencia si no se proporcionan argumentos posicionales o no
   todos los argumentos posicionales son objetos de tipo Future y no
   hay un bucle de eventos en ejecución.


Protección contra cancelación
=============================

awaitable asyncio.shield(aw)

   Protege un objeto esperable de ser "cancelado".

   Si *aw* es una corrutina, se programa automáticamente como una
   Tarea.

   La declaración:

      task = asyncio.create_task(something())
      res = await shield(task)

   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 de "something()", la cancelación no ocurrió. Aunque su
   invocador siga cancelado, por lo que la expresión "await" sigue
   generando un "CancelledError".

   Si "something()" se cancela por otros medios (es decir, desde
   dentro de sí mismo) eso también cancelaría "shield()".

   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:

      task = asyncio.create_task(something())
      try:
          res = await shield(task)
      except CancelledError:
          res = None

   Importante:

     Save a reference to tasks passed to this function, to avoid a
     task disappearing mid-execution. The event loop only keeps weak
     references to tasks. A task that isn't referenced elsewhere may
     get garbage collected at any time, even before it's done.

   Distinto en la versión 3.10: Removed the *loop* parameter.

   Obsoleto desde la versión 3.10: Se emite una advertencia de
   obsolescencia si *aw* no es un objeto similares a Futures y no hay
   un bucle de eventos en ejecución.


Tiempo agotado
==============

asyncio.timeout(delay)

   Return an asynchronous context manager that can be used to limit
   the amount of time spent waiting on something.

   *delay* can either be "None", or a float/int number of seconds to
   wait. If *delay* is "None", no time limit will be applied; this can
   be useful if the delay is unknown when the context manager is
   created.

   In either case, the context manager can be rescheduled after
   creation using "Timeout.reschedule()".

   Ejemplo:

      async def main():
          async with asyncio.timeout(10):
              await long_running_task()

   If "long_running_task" takes more than 10 seconds to complete, the
   context manager will cancel the current task and handle the
   resulting "asyncio.CancelledError" internally, transforming it into
   a "TimeoutError" which can be caught and handled.

   Nota:

     The "asyncio.timeout()" context manager is what transforms the
     "asyncio.CancelledError" into a "TimeoutError", which means the
     "TimeoutError" can only be caught *outside* of the context
     manager.

   Example of catching "TimeoutError":

      async def main():
          try:
              async with asyncio.timeout(10):
                  await long_running_task()
          except TimeoutError:
              print("The long operation timed out, but we've handled it.")

          print("This statement will run regardless.")

   The context manager produced by "asyncio.timeout()" can be
   rescheduled to a different deadline and inspected.

   class asyncio.Timeout(when)

      An asynchronous context manager for cancelling overdue
      coroutines.

      "when" should be an absolute time at which the context should
      time out, as measured by the event loop's clock:

      * If "when" is "None", the timeout will never trigger.

      * If "when < loop.time()", the timeout will trigger on the next
        iteration of the event loop.

         when() -> float | None

            Return the current deadline, or "None" if the current
            deadline is not set.

         reschedule(when: float | None)

            Reschedule the timeout.

         expired() -> bool

            Return whether the context manager has exceeded its
            deadline (expired).

   Ejemplo:

      async def main():
          try:
              # We do not know the timeout when starting, so we pass ``None``.
              async with asyncio.timeout(None) as cm:
                  # We know the timeout now, so we reschedule it.
                  new_deadline = get_running_loop().time() + 10
                  cm.reschedule(new_deadline)

                  await long_running_task()
          except TimeoutError:
              pass

          if cm.expired():
              print("Looks like we haven't finished on time.")

   Timeout context managers can be safely nested.

   Nuevo en la versión 3.11.

asyncio.timeout_at(when)

   Similar to "asyncio.timeout()", except *when* is the absolute time
   to stop waiting, or "None".

   Ejemplo:

      async def main():
          loop = get_running_loop()
          deadline = loop.time() + 20
          try:
              async with asyncio.timeout_at(deadline):
                  await long_running_task()
          except TimeoutError:
              print("The long operation timed out, but we've handled it.")

          print("This statement will run regardless.")

   Nuevo en la versión 3.11.

coroutine asyncio.wait_for(aw, timeout)

   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* es "None", se bloquea hasta que
   Future se completa.

   If a timeout occurs, it cancels the task and raises "TimeoutError".

   Para evitar la "cancelación" de la tarea , envuélvala en
   "shield()".

   La función esperará hasta que se cancele el Future, por lo que el
   tiempo de espera total puede exceder el *timeout*. Si ocurre una
   excepción durante la cancelación, se propaga.

   Si se cancela la espera, el Future *aw* también se cancela.

   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 TimeoutError:
              print('timeout!')

      asyncio.run(main())

      # Expected output:
      #
      #     timeout!

   Distinto en la versión 3.7: When *aw* is cancelled due to a
   timeout, "wait_for" waits for *aw* to be cancelled.  Previously, it
   raised "TimeoutError" immediately.

   Distinto en la versión 3.10: Removed the *loop* parameter.

   Distinto en la versión 3.11: Raises "TimeoutError" instead of
   "asyncio.TimeoutError".


Esperando primitivas
====================

coroutine asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)

   Run "Future" and "Task" instances in the *aws* iterable
   concurrently and block until the condition specified by
   *return_when*.

   The *aws* iterable must not be empty and generators yielding tasks
   are not accepted.

   Retorna dos conjuntos de Tareas/Futures: "(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.

   Note that this function does not raise "TimeoutError". Futures or
   Tasks that aren't done when the timeout occurs are simply returned
   in the second set.

   *return_when* indica cuándo debe retornar esta función. Debe ser
   una de las siguientes constantes:

   +----------------------------------------------------+----------------------------------------------------+
   | Constante                                          | Descripción                                        |
   |====================================================|====================================================|
   | asyncio.FIRST_COMPLETED                            | La función retornará cuando cualquier Future       |
   |                                                    | termine o se cancele.                              |
   +----------------------------------------------------+----------------------------------------------------+
   | asyncio.FIRST_EXCEPTION                            | The function will return when any future finishes  |
   |                                                    | by raising an exception. If no future raises an    |
   |                                                    | exception then it is equivalent to                 |
   |                                                    | "ALL_COMPLETED".                                   |
   +----------------------------------------------------+----------------------------------------------------+
   | asyncio.ALL_COMPLETED                              | La función retornará cuando todos los Futures      |
   |                                                    | terminen o se cancelen.                            |
   +----------------------------------------------------+----------------------------------------------------+

   A diferencia de "wait_for()", "wait()" no cancela los Futures
   cuando se produce un agotamiento de tiempo.

   Distinto en la versión 3.10: Removed the *loop* parameter.

   Distinto en la versión 3.11: Passing coroutine objects to "wait()"
   directly is forbidden.

asyncio.as_completed(aws, *, timeout=None)

   Run awaitable objects in the *aws* iterable concurrently.
   Generators yielding tasks are not accepted as *aws* iterable.
   Return an iterator of coroutines. Each coroutine returned can be
   awaited to get the earliest next result from the iterable of the
   remaining awaitables.

   Raises "TimeoutError" if the timeout occurs before all Futures are
   done.

   Ejemplo:

      for coro in as_completed(aws):
          earliest_result = await coro
          # ...

   Distinto en la versión 3.10: Removed the *loop* parameter.

   Obsoleto desde la versión 3.10: Se emite una advertencia de
   obsolescencia si no todos los objetos en espera en el iterable
   *aws* son objetos de tipo Future y no hay un bucle de eventos en
   ejecución.


Ejecutando en hilos
===================

coroutine asyncio.to_thread(func, /, *args, **kwargs)

   Ejecutar asincrónicamente la función *func* en un hilo separado.

   Cualquier *args y **kwargs suministrados para esta función se pasan
   directamente a *func*. Además, el current "contextvars.Context" se
   propaga, lo que permite acceder a las variables de contexto del
   subproceso del bucle de eventos en el subproceso separado.

   Retorna una corrutina que se puede esperar para obtener el
   resultado final de *func*.

   This coroutine function is primarily intended to be used for
   executing IO-bound functions/methods that would otherwise block the
   event loop if they were run in the main thread. For example:

      def blocking_io():
          print(f"start blocking_io at {time.strftime('%X')}")
          # Note that time.sleep() can be replaced with any blocking
          # IO-bound operation, such as file operations.
          time.sleep(1)
          print(f"blocking_io complete at {time.strftime('%X')}")

      async def main():
          print(f"started main at {time.strftime('%X')}")

          await asyncio.gather(
              asyncio.to_thread(blocking_io),
              asyncio.sleep(1))

          print(f"finished main at {time.strftime('%X')}")


      asyncio.run(main())

      # Expected output:
      #
      # started main at 19:50:53
      # start blocking_io at 19:50:53
      # blocking_io complete at 19:50:54
      # finished main at 19:50:54

   Directly calling "blocking_io()" in any coroutine would block the
   event loop for its duration, resulting in an additional 1 second of
   run time. Instead, by using "asyncio.to_thread()", we can run it in
   a separate thread without blocking the event loop.

   Nota:

     Due to the *GIL*, "asyncio.to_thread()" can typically only be
     used to make IO-bound functions non-blocking. However, for
     extension modules that release the GIL or alternative Python
     implementations that don't have one, "asyncio.to_thread()" can
     also be used for CPU-bound functions.

   Nuevo en la versión 3.9.


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 Future retornado será
   notificado. También se puede utilizar para cancelar la tarea en el
   bucle de eventos:

      try:
          result = future.result(timeout)
      except 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 o "None" 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.

asyncio.iscoroutine(obj)

   Return "True" if *obj* is a coroutine object.

   Nuevo en la versión 3.4.


Objeto Task
===========

class asyncio.Task(coro, *, loop=None, name=None, context=None)

   Un objeto "similar a Future" 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 Future, la Tarea suspende
   la ejecución de la corrutina y espera la finalización del Future.
   Cuando el Future *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 Future, 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 nivel "loop.create_task()" o
   "ensure_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ón "CancelledError" en
   la corrutina contenida. Si una corrutina está esperando en un
   objeto Future durante la cancelación, se cancelará el objeto
   Future.

   "cancelled()" se puede utilizar para comprobar si la Tarea fue
   cancelada. El método devuelve "True" si la corrutina contenida no
   suprimió la excepción "CancelledError" y se canceló realmente.

   "asyncio.Task" hereda de "Future" todas sus API excepto
   "Future.set_result()" y "Future.set_exception()".

   An optional keyword-only *context* argument allows specifying a
   custom "contextvars.Context" for the *coro* to run in. If no
   *context* is provided, the Task copies the current context and
   later runs its coroutine in the copied context.

   Distinto en la versión 3.7: Agregado soporte para el módulo
   "contextvars".

   Distinto en la versión 3.8: Added the *name* parameter.

   Obsoleto desde la versión 3.10: Se emite una advertencia de
   obsolescencia si no se especifica *loop* y no hay un bucle de
   eventos en ejecución.

   Distinto en la versión 3.11: Added the *context* parameter.

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

      The *file* argument is an I/O stream to which the output is
      written; by default output is written to "sys.stdout".

   get_coro()

      Retorna el objeto corrutina contenido por "Task".

      Nuevo en la versión 3.8.

   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.

   cancel(msg=None)

      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.

      The coroutine then has a chance to clean up or even deny the
      request by suppressing the exception with a "try" ... ...
      "except CancelledError" ... "finally" block. Therefore, unlike
      "Future.cancel()", "Task.cancel()" does not guarantee that the
      Task will be cancelled, although suppressing cancellation
      completely is not common and is actively discouraged.  Should
      the coroutine nevertheless decide to suppress the cancellation,
      it needs to call "Task.uncancel()" in addition to catching the
      exception.

      Distinto en la versión 3.9: Added the *msg* parameter.

      Distinto en la versión 3.11: The "msg" parameter is propagated
      from cancelled task to its awaiter.

      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ón
      "CancelledError" que se le ha lanzado.

   uncancel()

      Decrement the count of cancellation requests to this Task.

      Returns the remaining number of cancellation requests.

      Note that once execution of a cancelled task completed, further
      calls to "uncancel()" are ineffective.

      Nuevo en la versión 3.11.

      This method is used by asyncio's internals and isn't expected to
      be used by end-user code.  In particular, if a Task gets
      successfully uncancelled, this allows for elements of structured
      concurrency like Task Groups and "asyncio.timeout()" to continue
      running, isolating cancellation to the respective structured
      block. For example:

         async def make_request_with_timeout():
             try:
                 async with asyncio.timeout(1):
                     # Structured block affected by the timeout:
                     await make_request()
                     await make_another_request()
             except TimeoutError:
                 log("There was a timeout")
             # Outer code not affected by the timeout:
             await unrelated_code()

      While the block with "make_request()" and
      "make_another_request()" might get cancelled due to the timeout,
      "unrelated_code()" should continue running even in case of the
      timeout.  This is implemented with "uncancel()".  "TaskGroup"
      context managers use "uncancel()" in a similar fashion.

      If end-user code is, for some reason, suppresing cancellation by
      catching "CancelledError", it needs to call this method to
      remove the cancellation state.

   cancelling()

      Return the number of pending cancellation requests to this Task,
      i.e., the number of calls to "cancel()" less the number of
      "uncancel()" calls.

      Note that if this number is greater than zero but the Task is
      still executing, "cancelled()" will still return "False". This
      is because this number can be lowered by calling "uncancel()",
      which can lead to the task not being cancelled after all if the
      cancellation requests go down to zero.

      This method is used by asyncio's internals and isn't expected to
      be used by end-user code.  See "uncancel()" for more details.

      Nuevo en la versión 3.11.
