martes, 7 de abril de 2026

Python ASYNC (II). El event loop

1. Introducción

El event loop es el organizador de las tareas que se ejecutan dentro de é.

 El event loop normalmente se crea desde un una función síncrona con asyncio.run() 

def funcion_sync():
    resultado = asyncio.run(mi_funcion_async())
    print(resultado)


2. Entornos FastAPI (como FastHTML)

En este caso ya se ha creado un event loop. Para ello lo mas normal es utilizar funciones asíncronas.

Los puntos de partida para llamadas a las funciones suelen ser los "endpoints"(o urls) y conviene definirlas como asíncronas:

@app.post("/softprop/validate_login")
async def post_login(req, login: xmclasses.Login):
''' 1. Verifica usuario y contraseña ''' ldap_authenticated= await xmldap.autheticate_by_login(login.usuari, login.paraula_de_pass) if not ldap_authenticated: print ("returning to first login page") return fh.Div("Error en l'usuari o la paraula de pas", cls="alert alert-danger")


Pero puede haber casos que  "recojamos el event loop con asyncio.get_event_loop()  y ejcutemos funciones dendro de él con asyncio.run_coroutine_threadsafe() y recogiendo el resultado con .result() .

import asyncio

loop = asyncio.get_event_loop()
future = asyncio.run_coroutine_threadsafe(mi_funcion_async(), loop)
resultado = future.result()





Python ASYNC (I). Introducción

 1. Introducción

Async ejecuta una tarea mientras otra tarea está esperando. No es paraleleismo real pues no utiliza varios núcleos.


Veamos un ejemplo de programa sj́ncrono y su homólogo asíncrono.

La versión síncrona ejecuta el proceso tarea() dos veces que espera 2 segundos cada vez por tanto tarda 4 segundos

import time

def tarea():
    time.sleep(2)
    print("Hecho")

tarea()
tarea()


La versión asíncrona solo tarda 2 segundos pues cuando lanza la primera tarea(), no se espera a que termine para lanzar la segunda tarea() 


import asyncio

async def tarea():
    await asyncio.sleep(2)
    print("Hecho")

async def main():
    await asyncio.gather(
        tarea(),
        tarea()
    )

asyncio.run(main())


2. Cuando usar async?

  • Redes (HTTP, APIs)
  • Escaneo de red
  • I/O (archivos, sockets)
  • Bots, servidores

No sirve para:

  • CPU intensivo (usar multiprocessing mejor)
Librerías async importantes:
  • asyncio (core)
  • aiohttp (HTTP)
  • asyncssh (SSH)
  • aiomysql, asyncpg (DB)
  • aiofiles (archivos)

3. Conceptos clave

1. Las funciones asíncronas se definen con async def

async def mi_funcion():
    pass

2. El comando async ejecuta funciones asíncronas dentro de otras funciones asíncronas

async def mi_funcion():
    await my_async_func()

3. Para el main utilizar asyncio.run(my_async_fun()) (es el event loop)

if __name__ == "__main__":
    asyncio.run(run_from_yaml())

4. courutines, problematica de su ejecución. Si una función asíncrono no se la llama con async, entonces es una courutina y no se ejecuta aún

import asyncio

async def tarea():
    await asyncio.sleep(2)
    return 'hola'

async def main():
    #MALA llamada, a1 es una corrutina y no se ejecuta aún
    #  a1 es una referencia a la función tarea()
    a1= tarea()
 
    #BUENA llamada
    a2=await tarea()
    
asyncio.run(main())

5. ejecución de varias fuinciones (a la vez)con asyncio.gather

import asyncio

async def main():
    await asyncio.gather(tarea1(), tarea2(), tarea3()) 
    
asyncio.run(main())

6. await asyncio.sleep(1) para parar la ejecución dentro de una función asíncrona pero sin parar otras funciones que se ejecutan en paralelo

import asyncio

async def my_async_func():
    #Para 1 sefundo esta tarea solo y no el resto de funciones asíncronas
    await asyncio.sleep(1)