Corrotinas e Tarefas
********************

Esta seção descreve APIs assíncronas de alto nível para trabalhar com
corrotinas e tarefas.

* Coroutines

* Aguardáveis

* Executando um programa asyncio

* Criando Tarefas

* Dormindo

* Executando tarefas concorrentemente

* Protegendo contra cancelamento

* Tempo limite

* Primitivas de Espera

* Agendando a partir de outras Threads

* Instrospecção

* Objeto Task

* Corrotinas baseadas em gerador


Coroutines
==========

Coroutines declared with async/await syntax is the preferred way of
writing asyncio applications.  For example, the following snippet of
code (requires Python 3.7+) 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

Perceba que simplesmente chamar uma corrotina não irá agendá-la para
ser executada:

   >>> main()
   <coroutine object main at 0x1053bb7c8>

Para realmente executar uma corrotina, asyncio fornece três mecanismos
principais:

* A função: func:*asyncio.run* para executar a função "main()" do
  ponto de entrada no nível mais alto (veja o exemplo acima.)

* Aguardando uma corrotina. O seguinte trecho de código exibirá
  "hello" após esperar por 1 segundo e, em seguida, exibirá "world"
  após esperar por *outros* 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())

  Resultado esperado:

     started at 17:13:52
     hello
     world
     finished at 17:13:55

* A função "asyncio.create_task()" para executar corrotinas
  concorrentemente como "Tasks" asyncio.

  Vamo modificar o exemplo acima e executar duas corrotinas
  "say_after" *concorrentemente*:

     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')}")

  Perceba que a saída esperada agora mostra que o trercho de código é
  executado 1 segundo mais rápido do que antes:

     started at 17:14:32
     hello
     world
     finished at 17:14:34


Aguardáveis
===========

Dizemos que um objeto é um objeto **aguardável** se ele pode ser usado
em uma expressão "await". Muitas APIs asyncio são projetadas para
aceitar aguardáveis.

Existem três tipos principais de objetos *aguardáveis*:
**corrotinas**, **Tarefas**, e **Futuros**.

-[ Coroutines ]-

Corrotinas Python são *aguardáveis* e portanto podem ser aguardadas a
partir de outras corrotinas:

   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:

  Nesta documentação, o termo "corrotina" pode ser usado para dois
  conceitos intimamente relacionados:

  * uma *função de corrotina*: uma função "async def";

  * um *objeto de corrotina*: um objeto retornado ao chamar uma
    *função de corrotina*.

asyncio também suporta corrotinas legadas baseadas em geradores.

-[ Tarefas ]-

*Tarefas* são usadas para agendar corrotinas *concorrentemente*.

Quando uma corrotina é envolta em uma *tarefa* com funções como
"asyncio.create_task()", a corrotina é automaticamente agendada para
executar em breve:

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

Um "Future" é um objeto aguardável especial de **baixo nível** que
representa um **resultado eventual** de uma operação assíncrona.

Quando um objeto Future é *aguardado* isso significa que a corrotina
irá esperar até que o Future seja resolvido em algum outro local.

Objetos Future em asyncio são necessários para permitir que código
baseado em função de retorno seja utilizado com async/await.

Normalmente **não existe necessidade** em criar objetos Future no
nível de código da aplicação.

Objetos Future, algumas vezes expostos por bibliotecas e algumas APIs
asyncio, podem ser aguardados:

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

Um bom exemplo de uma função de baixo nível que retorna um objeto
Future é "loop.run_in_executor()".


Executando um programa asyncio
==============================

asyncio.run(coro, *, debug=False)

   Executa a *corrotina* *coro* e retorna o resultado.

   This function runs the passed coroutine, taking care of managing
   the asyncio event loop and *finalizing asynchronous generators*.

   Esta função não pode ser chamada quando outro ciclo de eventos
   asyncio está executando na mesma thread.

   Se *debug* for "True", o ciclo de eventos irá ser executado em modo
   debug.

   Esta função sempre cria um novo ciclo de eventos e fecha-o no
   final. Ela deve ser usada como um ponto de entrada principal para
   programas asyncio, e deve idealmente ser chamada apenas uma vez.

   Exemplo:

      async def main():
          await asyncio.sleep(1)
          print('hello')

      asyncio.run(main())

   Novo na versão 3.7: **Important:** this function has been added to
   asyncio in Python 3.7 on a *provisional basis*.


Criando Tarefas
===============

asyncio.create_task(coro)

   Envolva a corrotina *coro* em uma "Task" e agende sua execução.
   Retorne o objeto Task.

   A tarefa é executada no loop e retornada por "get_running_loop()",
   "RuntimeError" é levantado se não existir nenhum loop na thread
   atual.

   Esta função foi **adicionada no Python 3.7**. Antes do Python 3.7,
   a função de baixo nível "asyncio.ensure_future()" pode ser usada ao
   invés:

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

   Novo na versão 3.7.


Dormindo
========

coroutine asyncio.sleep(delay, result=None, *, loop=None)

   Bloqueia por *delay* segundos.

   Se *result* é fornecido, é retornado para o autor da chamada quando
   a corrotina termina.

   "sleep()" sempre suspende a tarefa atual, permitindo que outras
   tarefas sejam executadas.

   The *loop* argument is deprecated and scheduled for removal in
   Python 3.10.

   Exemplo de uma corrotina exibindo a data atual a 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())


Executando tarefas concorrentemente
===================================

awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)

   Executa objetos aguardáveis na sequência *aws* de forma
   *concorrente*.

   Se qualquer aguardável em *aws* é uma corrotina, ele é
   automaticamente agendado como uma Tarefa.

   Se todos os aguardáveis forem concluídos com sucesso, o resultado é
   uma lista agregada de valores retornados. A ordem dos valores
   resultantes corresponde a ordem dos aguardáveis em *aws*.

   Se *return_exceptions* for "False" (valor padrão), a primeira
   exceção levantada é imediatamente propagada para a tarefa que
   espera em "gather()". Outros aguardáveis na sequência *aws* **não
   serão cancelados** e irão continuar a executar.

   Se *return_exceptions* for "True", exceções são tratadas da mesma
   forma que que resultados com sucesso, e agregadas na lista de
   resultados.

   Se "gather()" for *cancelado*, todos os aguardáveis que foram
   submetidos (que não foram concluídos ainda) também são
   *cancelados*.

   Se qualquer Task ou Future da sequência *aws* for *cancelado*, ele
   é tratado como se tivesse levantado "CancelledError" -- a chamada
   para "gather()" **não** é cancelada neste caso. Isso existe para
   prevenir que o cancelamento de uma Task/Future submetida ocasione
   outras Tasks/Futures a serem cancelados.

   Exemplo:

      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

   Alterado na versão 3.7: Se *gather* por si mesmo for cancelado, o
   cancelamento é propagado independente de *return_exceptions*.


Protegendo contra cancelamento
==============================

awaitable asyncio.shield(aw, *, loop=None)

   Protege um objeto aguardável de ser "cancelado".

   Se *aw* é uma corrotina, ela é automaticamente agendada como uma
   Task.

   A instrução:

      res = await shield(something())

   is equivalent to:

      res = await something()

   *exceto* que se a corrotina contendo-a for cancelada, a Task
   executando em "something()" não é cancelada. Do ponto de vista de
   "something()", o cancelamento não aconteceu. Apesar do autor da
   chamada ainda estar cancelado, então a expressão "await" ainda
   levanta um "CancelledError".

   Se "something()" é cancelada por outros meios (isto é, dentro ou a
   partir de si mesma) isso também iria cancelar "shield()".

   Se for desejado ignorar completamente os cancelamentos (não
   recomendado) a função "shield()" deve ser combinada com uma
   cláusula try/except, conforme abaixo:

      try:
          res = await shield(something())
      except CancelledError:
          res = None


Tempo limite
============

coroutine asyncio.wait_for(aw, timeout, *, loop=None)

   Espera o aguardável *aw* concluir sem ultrapassar o tempo limite
   "timeout".

   Se *aw* é uma corrotina, ela é automaticamente agendada como uma
   Task.

   *timeout* pode ser "None", ou um ponto flutuante, ou um número
   inteiro de segundos para aguardar. Se *timeout* é "None", aguarda
   até o future encerrar.

   Se o tempo limite timeout for atingido, ele cancela a tarefa e
   levanta "asyncio.TimeoutError".

   Para evitar o "cancelamento" da tarefa, envolva-a com "shield()".

   The function will wait until the future is actually cancelled, so
   the total wait time may exceed the *timeout*.

   Se ele for cancelado, o future *aw* também é cancelado.

   The *loop* argument is deprecated and scheduled for removal in
   Python 3.10.

   Exemplo:

      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!

   Alterado na versão 3.7: Quando *aw* é cancelado devido a um tempo
   limite, "wait_for" aguarda que *aw* seja cancelado. Anteriormente,
   ele levantava "asyncio.TimeoutError" imediatamente.


Primitivas de Espera
====================

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

   Executa objetos aguardáveis no *aws* definidos concorrentemente e
   bloqueia até atingir a condição especificada por *return_when*.

   Se qualquer aguardável em *aws* for uma corrotina, ela é
   automaticamente agendada como uma tarefa. Passar objetos que são
   corrotinas para "wait()" diretamente está descontinuado, pois leva
   a comportamentos confusos.

   Retorna dois conjuntos de Tarefas/Futuros: "(done, pending)".

   Utilização:

      done, pending = await asyncio.wait(aws)

   The *loop* argument is deprecated and scheduled for removal in
   Python 3.10.

   *timeout* (um ponto flutuante ou inteiro), se especificado, pode
   ser usado para controlar o número máximo de segundos para aguardar
   antes de retornar.

   Perceba que esta função não levanta "asyncio.TimeoutError". Futuros
   ou Tarefas que não estão concluídas quando o tempo limite é
   excedido são simplesmente retornadas no segundo conjunto.

   *return_when* indica quando esta função deve retornar. Ele deve ser
   uma das seguintes constantes:

   +-------------------------------+------------------------------------------+
   | Constante                     | Description (descrição)                  |
   |===============================|==========================================|
   | "FIRST_COMPLETED"             | A função irá retornar quando qualquer    |
   |                               | futuro terminar ou for cancelado.        |
   +-------------------------------+------------------------------------------+
   | "FIRST_EXCEPTION"             | A função irá retornar quando qualquer    |
   |                               | futuro encerrar levantando uma exceção.  |
   |                               | Se nenhum futuro levantar uma exceção,   |
   |                               | então é equivalente a "ALL_COMPLETED".   |
   +-------------------------------+------------------------------------------+
   | "ALL_COMPLETED"               | A função irá retornar quando todos os    |
   |                               | futuros encerrarem ou forem cancelados.  |
   +-------------------------------+------------------------------------------+

   Diferente de "wait_for()", "wait()" não cancela os futuros quando
   um tempo limite é atingido.

   Nota:

     "wait()" agenda corrotinas como Tarefas automaticamente e
     posteriormente retorna esses objetos Tasks criados implicitamente
     em conjuntos "(done, pending)". Portanto o seguinte código não
     irá funcionar como esperado:

        async def foo():
            return 42

        coro = foo()
        done, pending = await asyncio.wait({coro})

        if coro in done:
            # This branch will never be run!

     Aqui está a forma como o trecho de código acima pode ser
     consertado:

        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.

     Passar objetos corrotina para "wait()" diretamente foi
     descontinuado.

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

   Run awaitable objects in the *aws* set concurrently.  Return an
   iterator of "Future" objects. Each Future object returned
   represents the earliest result from the set of the remaining
   awaitables.

   Levanta "asyncio.TimeoutError" se o tempo limite ocorrer antes que
   todos os futuros tenham encerrado.

   Exemplo:

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


Agendando a partir de outras Threads
====================================

asyncio.run_coroutine_threadsafe(coro, loop)

   Envia uma corrotina para o ciclo de eventos fornecido. Seguro para
   thread.

   Retorna um "concurrent.futures.Future" para aguardar pelo resultado
   de outra thread do sistema operacional.

   Esta função destina-se a ser chamada partir de uma thread diferente
   do sistema operacional, da qual o ciclo de eventos está executando.
   Exemplo:

      # 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

   Se uma exceção for levantada na corrotina, o Future retornado será
   notificado. Isso também pode ser usado para cancelar a tarefa no
   ciclo 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}')

   Veja a seção concorrência e multithreading da documentação.

   Ao contrário de outras funções asyncio, esta função requer que o
   argumento *loop* seja passado explicitamente.

   Novo na versão 3.5.1.


Instrospecção
=============

asyncio.current_task(loop=None)

   Retorna a instância "Task" atualmente em execução, ou "None" se
   nenhuma tarefa estiver executando.

   Se *loop* for "None", então "get_running_loop()" é usado para obter
   o laço atual.

   Novo na versão 3.7.

asyncio.all_tasks(loop=None)

   Retorna um conjunto de objetos "Task" ainda não concluídos a serem
   executados pelo laço.

   Se *loop* for "None", então "get_running_loop()" é usado para obter
   o laço atual.

   Novo na versão 3.7.


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

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

   Um objeto "similar a Futuro" que executa uma corrotina Python. Não
   é seguro para thread.

   Tarefas são usadas para executar corrotinas em ciclo de eventos. Se
   uma corrotina espera por um Futuro, a Tarefa suspende a execução da
   corrotina e aguarda a conclusão do Futuro. Quando o futuro é
   *concluído*, a execução da corrotina contida é retomada.

   Ciclo de eventos usam agendamento cooperativo: um ciclo de evento
   executa uma Tarefa de cada vez. Enquanto uma Tarefa aguarda de um
   Futuro, o ciclo de eventos executa outras Tarefas, funções de
   retorno, ou executa operações de IO.

   Use a função de alto nível "asyncio.create_task()" para criar
   Tarefas, ou as funções de baixo nível "loop.create_task()" ou
   "ensure_future()". Instanciação manual de Tarefas é desencorajado.

   Para cancelar uma Tarefa em execução, use o método "cancel()".
   Chamar ele fará com que a Tarefa levante uma exceção
   "CancelledError" dentro da corrotina contida. Se a corrotina
   estiver esperando por um objeto Future durante o cancelamento, o
   objeto Future será cancelado.

   "cancelled()" pode ser usado para verificar se a Tarefa foi
   cancelada. O método retorna "True" se a corrotina envolta não
   suprimiu a exceção "CancelledError" e foi na verdade cancelada.

   "asyncio.Task" herda de "Future" todas as suas APIs exceto
   "Future.set_result()" e "Future.set_exception()".

   Tarefas suportam o módulo "contextvars". Quando a Tarefa é criada,
   ela copia o contexto atual e posteriormente executa sua corrotina
   no contexto copiado.

   Alterado na versão 3.7: Adicionado suporte para o módulo
   "contextvars".

   cancel()

      Solicita o cancelamento da Tarefa.

      Isto prepara para uma exceção "CancelledError" ser lançada na
      corrotina contida no próximo ciclo do ciclo de eventos.

      A corrotina então tem uma chance de limpar ou até mesmo negar a
      requisição, suprimindo a exceção com um bloco "try" ... ...
      "except CancelledError" ... "finally". Portanto, ao contrário de
      "Future.cancel()", "Task.cancel()" não garante que a Tarefa será
      cancelada, apesar que suprimir o cancelamento completamente não
      é comum, e é ativamente desencorajado.

      O seguinte exemplo ilustra como corrotinas podem interceptar o
      cancelamento de requisições:

         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" se a Tarefa for *cancelada*.

      A Tarefa é *cancelada* quando o cancelamento foi requisitado com
      "cancel()" e a corrotina contida propagou a exceção
      "CancelledError" gerada nela.

   done()

      Retorna "True" se a Tarefa estiver *concluída*.

      Uma Tarefa está *concluída* quando a corrotina contida retornou
      um valor, ou levantou uma exceção, ou a Tarefa foi cancelada.

   result()

      Retorna o resultado da Tarefa.

      Se a Tarefa estiver *concluída*, o resultado da corrotina
      contida é retornado (ou se a corrotina levantou uma exceção,
      essa exceção é re-levantada.)

      Se a Tarefa foi *cancelada*, este método levanta uma exceção
      "CancelledError".

      Se o resultado da Tarefa não estiver disponível ainda, este
      método levanta uma exceção "InvalidStateError".

   exception()

      Retorna a exceção de uma Tarefa.

      Se a corrotina contida levantou uma exceção, essa exceção é
      retornada. Se a corrotina contida retornou normalmente, este
      método retorna "None".

      Se a Tarefa foi *cancelada*, este método levanta uma exceção
      "CancelledError".

      Se a Tarefa não estiver *concluída* ainda, este método levanta
      uma exceção "InvalidStateError".

   add_done_callback(callback, *, context=None)

      Adiciona uma função de retorno para ser executada quando a
      Tarefa estiver *concluída*.

      Este método deve ser usado apenas em código de baixo nível
      baseado em funções de retorno.

      Veja a documentação para "Future.add_done_callback()" para mais
      detalhes.

   remove_done_callback(callback)

      Remove *callback* da lista de funções de retorno.

      Este método deve ser usado apenas em código de baixo nível
      baseado em funções de retorno.

      Veja a documentação do método "Future.remove_done_callback()"
      para mais detalhes.

   get_stack(*, limit=None)

      Retorna a lista de frames da pilha para esta Tarefa.

      Se a corrotina contida não estiver concluída, isto retorna a
      pilha onde ela foi suspensa. Se a corrotina foi concluída com
      sucesso ou foi cancelada, isto retorna uma lista vazia. Se a
      corrotina foi terminada por uma exceção, isto retorna a lista de
      frames do traceback (situação da pilha de execução).

      Os quadros são sempre ordenados dos mais antigos para os mais
      recentes.

      Apenas um frame da pilha é retornado para uma corrotina
      suspensa.

      O argumento opcional *limit* define o o número de frames máximo
      para retornar; por padrão todos os frames disponíveis são
      retornados. O ordenamento da lista retornada é diferente
      dependendo se uma pilha ou um traceback (situação da pilha de
      execução) é retornado: os frames mais recentes de uma pilha são
      retornados, mas os frames mais antigos de um traceback são
      retornados. (Isso combina com o comportamento do módulo
      traceback.)

   print_stack(*, limit=None, file=None)

      Exibe a pilha ou situação da pilha de execução para esta Tarefa.

      Isto produz uma saída similar a do módulo traceback para frames
      recuperados por "get_stack()".

      O argumento *limit* é passado para "get_stack()" diretamente.

      O argumento *file* é um fluxo de entrada e saída para o qual a
      saída é escrita; por padrão a saída é escrita para "sys.stderr".

   classmethod all_tasks(loop=None)

      Return a set of all tasks for an event loop.

      By default all tasks for the current event loop are returned. If
      *loop* is "None", the "get_event_loop()" function is used to get
      the current loop.

      This method is **deprecated** and will be removed in Python 3.9.
      Use the "asyncio.all_tasks()" function instead.

   classmethod current_task(loop=None)

      Return the currently running task or "None".

      If *loop* is "None", the "get_event_loop()" function is used to
      get the current loop.

      This method is **deprecated** and will be removed in Python 3.9.
      Use the "asyncio.current_task()" function instead.


Corrotinas baseadas em gerador
==============================

Nota:

  Suporte para corrotinas baseadas em gerador está **descontinuado** e
  agendado para ser removido no Python 3.10.

Corrotinas baseadas em gerador antecedem a sintaxe async/await. Elas
são geradores Python que usam expressões "yield from" para aguardar
Futuros e outras corrotinas.

Corrotinas baseadas em gerador devem ser decoradas com
"@asyncio.coroutine", apesar disso não ser forçado.

@asyncio.coroutine

   Decorador para marcar corrotinas baseadas em gerador.

   Este decorador permite que corrotinas legadas baseadas em gerador
   sejam compatíveis com código async/await:

      @asyncio.coroutine
      def old_style_coroutine():
          yield from asyncio.sleep(1)

      async def main():
          await old_style_coroutine()

   This decorator is **deprecated** and is scheduled for removal in
   Python 3.10.

   Este decorador não deve ser usado para corrotinas "async def".

asyncio.iscoroutine(obj)

   Retorna "True" se *obj* é um objeto corrotina.

   Este método é diferente de "inspect.iscoroutine()" porque ele
   retorna "True" para corrotinas baseadas em gerador.

asyncio.iscoroutinefunction(func)

   Retorna "True" se *func* é uma função de corrotina.

   Este método é diferente de "inspect.iscoroutinefunction()" porque
   ele retorna "True" para funções de corrotina baseadas em gerador,
   decoradas com "@coroutine".
