8.3. El bucle de eventos¶
El bucle de eventos es el motor que asyncio ejecuta por debajo. Mantiene una lista de cada tarea del programa, pide a cada una que se ejecute hasta su siguiente await y pasa a la siguiente tarea lista. Cuando no hay tareas listas, espera —esa espera real es lo que deja la CPU disponible para que el firmware ejecute otras cosas y para que entren en acción los modos de ahorro de energía— hasta que algo que una tarea estaba esperando queda disponible, momento en el que reanuda esa tarea. Y así indefinidamente.
La mayoría de las aplicaciones nunca interactúan directamente con el bucle. El bucle es una consecuencia de llamar a asyncio.run(); la aplicación escribe corrutinas, las planifica como tareas y el bucle hace el resto.
8.3.1. Qué hace realmente asyncio.run()¶
Una sola llamada:
asyncio.run(main())
es la forma abreviada de una secuencia más larga que el bucle gestiona en nombre de la aplicación:
Crear el bucle de eventos si no existe ya.
Envolver la corrutina suministrada en una tarea y planificarla como punto de entrada de nivel superior del bucle.
Ejecutar el bucle —recorrer las tareas listas, esperar cuando no hay ninguna lista, reanudar tareas cuando sus awaits se completan— hasta que la tarea de nivel superior retorna o lanza una excepción.
Cancelar cualquier tarea creada por la aplicación que siga ejecutándose.
Devolver lo que la corrutina de nivel superior haya devuelto (o volver a lanzar lo que haya lanzado).
8.3.2. Un único bucle por programa¶
El asyncio de MicroPython tiene un solo bucle de eventos, y punto. No se puede crear un bucle nuevo, ni anidar un bucle dentro de otro. Llamar a asyncio.run() desde dentro de una corrutina que ya se está ejecutando en el bucle es un error; el bucle ya está ahí, y la corrutina solo tiene que hacer await sobre lo que quisiera iniciar.
En la práctica, la regla es la misma que la línea de cierre de la página anterior: hay exactamente una llamada a asyncio.run() por programa, en lo más alto, con una única async def main() detrás. Todo lo demás vive dentro de main.
8.3.3. Acceso directo al bucle¶
Para los raros casos en que una aplicación necesita tocar el bucle en sí —sobre todo diagnósticos y manejadores de excepciones—, asyncio.get_event_loop() devuelve el objeto Loop. A partir de ahí, la aplicación puede instalar un manejador de excepciones personalizado, inspeccionar qué está haciendo el bucle o (muy de vez en cuando) llamar directamente a create_task() en lugar de a asyncio.create_task() (son la misma operación).
El conjunto completo de métodos que expone Loop —run_forever(), stop(), set_exception_handler() y el resto— se trata en la página control del bucle más adelante en esta sección. Hasta entonces, asyncio.run(main()) es todo lo que una aplicación necesita.