Comenzando un bucle infinito desde una función asíncrona.
Estoy intentando consultar periódicamente una API. La estructura de mi código es la siguiente:
async def QueryData():
print(datetime.datetime.now())
async def main():
await TestApiConnection()
scheduler = AsyncIOScheduler(timezone="Europe/Berlin")
scheduler.add_job(QueryData, 'cron', minute='0-59')
scheduler.start()
try:
asyncio.get_event_loop().run_forever()
except (KeyboardInterrupt, SystemExit):
pass
scheduler.shutdown(wait=False)
if name == "main":
asyncio.run(main())
Se produce el siguiente error si ejecuto esto:
en runforever
assert self.selfreadingfuture is None AssertionError
Si convierto la función principal en una función síncrona, el trabajo QueryData comienza, pero el resultado de TestApiConnection ya no se espera.
async def QueryData():
print(datetime.datetime.now())
def main():
TestApiConnection()
…
if name == "main":
main()
¿Cómo inicio el trabajo desde el método principal asíncrono? ¿O debería reestructurar el código?
davy.ai
El mensaje de error sugiere que el objeto future utilizado por el ciclo de eventos para realizar operaciones de E / S no está en el estado correcto. Esto puede ser causado por un uso incorrecto de coroutines o llamadas de bloqueo dentro del contexto asíncrono.
Para iniciar el trabajo periódico desde la función principal asíncrona, se puede utilizar el método
create_task
proporcionado por el ciclo de eventos para programar la coroutine como una tarea. Esto se puede hacer de la siguiente manera:Esto programa el método
_run_jobs
del planificador como una tarea, que iniciará el trabajo periódico utilizando el horariocron
especificado. Luego se espera indefinidamente el ciclo de eventos utilizando un objetoasyncio.Event
, y el planificador se apaga en la salida utilizando el bloquefinally
.Alternativamente, se podría reestructurar el código para utilizar una función principal síncrona y esperar la llamada
TestApiConnection
utilizandoasyncio.to_thread
de la siguiente manera:Esto utiliza
asyncio.to_thread
para llamar a la función de bloqueoTestApiConnection
en un hilo separado, permitiendo que el ciclo de eventos continúe ejecutándose mientras se establece la conexión. El resto del código puede estructurarse de manera similar al primer ejemplo.Vale la pena señalar que ambas soluciones requieren algún tipo de llamada de bloqueo o retraso dentro de la función
QueryData
para evitar que ocupe todo el ciclo de eventos. En el ejemplo anterior, se utiliza una llamada aasyncio.sleep
para simular alguna forma de trabajo realizado.