8.3. O event loop

O event loop é o motor que o asyncio executa por baixo. Ele mantém uma lista de toda task no programa, pede a cada uma que execute até seu próximo await e segue para a próxima task pronta. Quando não há tasks prontas, ele espera – a espera de fato é o que torna a CPU disponível para o firmware executar outras coisas e para que os sleeps de economia de energia entrem em ação – até que algo que uma task estava aguardando se torne disponível, e então retoma essa task. Repita para sempre.

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

8.3.1. O que asyncio.run() realmente faz

Uma única chamada:

asyncio.run(main())

é uma forma abreviada de uma sequência mais longa que o loop gerencia em nome da aplicação:

  1. Criar o event loop, caso ele ainda não exista.

  2. Encapsular a corrotina fornecida em uma task e escaloná-la como o ponto de entrada de nível superior do loop.

  3. Executar o loop – percorrer as tasks prontas, esperar quando nenhuma estiver pronta, retomar as tasks quando seus awaits forem concluídos – até que a task de nível superior retorne ou lance uma exceção.

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

  5. Retornar o que quer que a corrotina de nível superior tenha retornado (ou relançar o que quer que ela tenha lançado).

8.3.2. Um único loop por programa

O asyncio do MicroPython tem um event loop, ponto final. Não há como criar um novo loop, e não há como aninhar um loop dentro de outro. Chamar asyncio.run() de dentro de uma corrotina que já está em execução no loop é um erro; o loop já está lá, e a corrotina só precisa fazer await em qualquer coisa que ela quisesse iniciar.

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

8.3.3. Acesso direto ao loop

Para os raros casos em que uma aplicação precisa tocar no próprio loop – principalmente diagnósticos e manipuladores de exceções – asyncio.get_event_loop() retorna o objeto Loop. A partir daí, a aplicação pode instalar um manipulador de exceções personalizado, inspecionar o que o loop está fazendo 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 demais – é abordado na página controle do loop mais adiante nesta seção. Até lá, asyncio.run(main()) é tudo o que uma aplicação precisa.