8.3. O ciclo de eventos

O ciclo de eventos é o motor que o asyncio executa por baixo. Mantém uma lista de cada tarefa no programa, pede a cada uma que execute até ao próximo await dessa tarefa, e passa para a próxima tarefa pronta. Quando não há tarefas prontas, aguarda – a espera real é o que torna o CPU disponível para o firmware executar outras coisas e para os sleeps de poupança de energia entrarem em ação – até que algo por que uma tarefa estava à espera fique disponível, depois retoma essa tarefa. Repete indefinidamente.

A maioria das aplicações nunca interage diretamente com o ciclo. O ciclo é uma consequência de chamar asyncio.run(); a aplicação escreve corrotinas, agenda-as como tarefas, e o ciclo faz o resto.

8.3.1. O que asyncio.run() faz na realidade

Uma única chamada:

asyncio.run(main())

é uma abreviatura de uma sequência mais longa que o ciclo gere em nome da aplicação:

  1. Criar o ciclo de eventos se ainda não existir.

  2. Envolver a corrotina fornecida numa tarefa e agendá-la como ponto de entrada de nível superior do ciclo.

  3. Executar o ciclo – percorrer as tarefas prontas, aguardar quando nenhuma estiver pronta, retomar as tarefas quando os seus awaits concluírem – até que a tarefa de nível superior retorne ou lance uma exceção.

  4. Cancelar quaisquer tarefas que a aplicação criou e que ainda estejam em execução.

  5. Devolver o que a corrotina de nível superior devolveu (ou relançar o que ela lançou).

8.3.2. Um ciclo por programa

O asyncio do MicroPython tem um ciclo de eventos, ponto final. Não é possível criar um ciclo novo, nem aninhar um ciclo dentro de outro. Chamar asyncio.run() dentro de uma corrotina que já está a correr no ciclo é um erro; o ciclo já existe, e a corrotina simplesmente precisa de aguardar pelo que queria iniciar.

Na prática, a regra é a mesma que a linha de fecho da página anterior: existe exatamente uma chamada a asyncio.run() por programa, no topo, com um único async def main() por trás. Tudo o resto vive dentro de main.

8.3.3. Acesso direto ao ciclo

Para os casos raros em que uma aplicação precisa de tocar no próprio ciclo – principalmente diagnósticos e gestores de exceções – asyncio.get_event_loop() devolve o objeto Loop. A partir daí, a aplicação pode instalar um gestor de exceções personalizado, inspecionar o que o ciclo está a fazer, ou (muito ocasionalmente) chamar create_task() diretamente em vez de asyncio.create_task() (são a mesma operação).

O conjunto completo de métodos que Loop expõe – run_forever(), stop(), set_exception_handler(), e os restantes – é abordado na página de controlo do ciclo mais adiante nesta secção. Até lá, asyncio.run(main()) é tudo o que uma aplicação precisa.