Los timeouts y la cancelación en asyncio impiden que una operación lenta bloquee una aplicación indefinidamente. La cancelación es cooperativa: la tarea recibe CancelledError en el siguiente punto de espera y puede liberar recursos. El código debe respetar esa señal para que la concurrencia estructurada funcione.
Empieza por la guía de async y await en Python si no conoces las corrutinas. Para tareas hijas relacionadas, consulta asyncio.TaskGroup.
Limita un bloque con asyncio.timeout
Python 3.11 o superior incluye asyncio.timeout() para aplicar un único plazo a varias operaciones:
import asyncio
async def cargar_pagina():
try:
async with asyncio.timeout(2.5):
conexion = await abrir_conexion()
return await conexion.recibir()
except TimeoutError:
return None
Captura TimeoutError fuera del bloque. Internamente, el context manager cancela la tarea y transforma CancelledError al salir. Para un plazo absoluto, calcula con el reloj monotónico del event loop y usa asyncio.timeout_at().
wait_for limita un awaitable
resultado = await asyncio.wait_for(consultar_api(), timeout=2)
Cuando vence el tiempo, wait_for cancela el awaitable y espera a que termine su cancelación. El tiempo real puede superar dos segundos si la limpieza tarda. asyncio.wait, en cambio, devuelve conjuntos de tareas terminadas y pendientes sin cancelar automáticamente las pendientes.
Libera recursos con finally
async def consumir(cola):
recurso = await abrir_recurso()
try:
while True:
item = await cola.get()
await procesar(item)
cola.task_done()
finally:
await recurso.cerrar()
Si capturas asyncio.CancelledError para registrar o limpiar, ejecuta raise después. Ocultarlo puede provocar comportamientos inesperados en TaskGroup y asyncio.timeout().
async def worker():
try:
await ejecutar()
except asyncio.CancelledError:
logger.info("worker cancelado")
raise
Cancela y espera la tarea
Solicitar cancelación no significa que la tarea ya se haya detenido:
tarea = asyncio.create_task(worker())
try:
await usar_tarea(tarea)
finally:
tarea.cancel()
try:
await tarea
except asyncio.CancelledError:
pass
Esperar permite que los bloques finally internos se ejecuten y evita tareas huérfanas. Los servicios también necesitan un plazo para el apagado y deben registrar las limpiezas que no terminan.
Usa shield con moderación
asyncio.shield() evita que la cancelación del llamador se propague a una tarea interna, pero el llamador sigue recibiendo CancelledError. Puede servir para terminar una escritura crítica ya iniciada. Conserva una referencia fuerte a la tarea y define quién observará su resultado o excepción.
No uses shield para ocultar una arquitectura sin límites. Las operaciones externas necesitan timeout propio y el apagado necesita un presupuesto total.
Prueba los caminos lentos
Prueba finalización normal, timeout, cancelación externa y errores durante la limpieza. Comprueba que conexiones, locks y elementos de cola se liberen. Usa sincronización controlada en las pruebas, no pausas largas que vuelvan inestable la suite.
La documentación oficial de tareas y timeouts de asyncio, consultada el 28 de julio de 2026, explica la conversión a TimeoutError, la propagación y los límites de shield.