Lección 19 · 19 de 20
asyncio con async y await · Python
Programación asíncrona en Python con asyncio: corrutinas con async y await, gather para tareas a la vez, sleep(0) y wait_for con tiempo límite.

Una función con `async def` es una corrutina: al llamarla no se ejecuta, sino que devuelve un objeto que el bucle de eventos de asyncio irá ejecutando. asyncio.run(main()) crea ese bucle, ejecuta main() y lo cierra al acabar.
`await` espera a otra corrutina y, mientras tanto, cede el turno. check_stock simula una consulta a un almacén con await asyncio.sleep(...): mientras Madrid «tarda», el bucle atiende a Bilbao y a Sevilla. Por eso las tres preguntas salen seguidas y las respuestas llegan por orden de rapidez: Bilbao (0,01 s), Sevilla y Madrid. Todo tarda lo que la más lenta, no la suma.
`asyncio.gather()` lanza varias corrutinas a la vez y espera a todas. Aunque terminen en otro orden, devuelve los resultados en el orden en que se pasaron, así que results empieza por Madrid.
await asyncio.sleep(0) no espera nada: solo devuelve el control al bucle. Con él, packer y printer se turnan en cada trabajo. Ese es el modelo de asyncio: un solo hilo y cambios de tarea solo en los await, de modo que el orden es predecible.
`asyncio.wait_for(..., timeout=0.05)` pone un tiempo límite: la operación de un segundo se cancela y salta TimeoutError, que el programa captura. asyncio brilla con muchas esperas de red o de disco; para cálculos pesados no aporta velocidad.
Código · main.py
import asyncio# Three warehouses answer a stock query; each one takes a different timeDELAY = {"Madrid": 0.03, "Bilbao": 0.01, "Sevilla": 0.02}STOCK = {"Madrid": 12, "Bilbao": 0, "Sevilla": 7}async def check_stock(city, product): print(f"asking {city} about {product}...") await asyncio.sleep(DELAY[city]) # waits without blocking the rest print(f" {city} answered") return city, STOCK[city]async def worker(name, jobs): for job in jobs: print(f"{name}: {job}") await asyncio.sleep(0) # hand control back to the loopasync def main(): # gather runs them at the same time and keeps the order of the results results = await asyncio.gather(*(check_stock(c, "chair") for c in DELAY)) print("Results:", results) # Two coroutines taking turns: each await is a switch point await asyncio.gather(worker("packer", ["box #1", "box #2"]), worker("printer", ["label #1", "label #2"])) try: # give up if it takes too long await asyncio.wait_for(asyncio.sleep(1), timeout=0.05) except TimeoutError: print("Slow supplier: timed out")asyncio.run(main())Salida · python3 main.py
asking Madrid about chair...
asking Bilbao about chair...
asking Sevilla about chair...
Bilbao answered
Sevilla answered
Madrid answered
Results: [('Madrid', 12), ('Bilbao', 0), ('Sevilla', 7)]
packer: box #1
printer: label #1
packer: box #2
printer: label #2
Slow supplier: timed outPara recordar
async defdefine una corrutina;awaitespera sin bloquear.asyncio.run()arranca el bucle de eventos.gatherejecuta varias a la vez y mantiene el orden de los resultados.- Las tareas solo se alternan en los
await. wait_forcancela lo que pasa del tiempo límite.
Ejercicio
Añada un cuarto almacén, Valencia, con 0,005 s de retraso y 3 unidades, y compruebe en qué posición responde y en cuál aparece en results.