asyncio: corutinas, tasks y timeouts en Python

Actualizado el: 20/08/2026
Tempo de leitura: 4 minutos
Programação assíncrona com asyncio em Python

Muchos programas pasan gran parte del tiempo esperando: una respuesta HTTP, una consulta, un temporizador o datos de una conexión. Durante esa espera, un programa síncrono puede permanecer bloqueado. asyncio en Python ofrece una forma estructurada de ejecutar varias operaciones de espera de manera cooperativa en un solo hilo.

En esta guía aprenderás coroutines, el event loop, tareas, espera concurrente, timeouts, cancelación, colas y manejo de errores. Es recomendable comprender primero las funciones en Python.

Qué problema resuelve asyncio

La programación asíncrona es útil cuando las tareas esperan con frecuencia eventos externos. Una coroutine puede pausarse en await y permitir que otra avance. Esto es concurrencia, no necesariamente ejecución paralela.

Tu primera coroutine

import asyncio

async def saludar():
    print("Hola")
    await asyncio.sleep(1)
    print("Adiós")

asyncio.run(saludar())

async def define una coroutine. Al llamarla obtienes un objeto coroutine; asyncio.run() crea el event loop, ejecuta la coroutine principal y cierra el bucle.

await no bloquea todo el programa

asyncio.sleep() suspende solamente la coroutine actual. Durante ese tiempo, el event loop puede ejecutar otras tareas. En cambio, una llamada bloqueante detiene todas las coroutines que comparten el mismo hilo.

Ejecutar operaciones concurrentes

import asyncio

async def obtener(nombre, demora):
    print(f"Iniciando {nombre}")
    await asyncio.sleep(demora)
    return f"{nombre} terminado"

async def main():
    resultados = await asyncio.gather(
        obtener("usuarios", 1.0),
        obtener("pedidos", 1.5),
        obtener("productos", 0.5),
    )
    print(resultados)

asyncio.run(main())

El tiempo total se aproxima a la operación más lenta, no a la suma de todas, porque las esperas se superponen.

Crear tareas

import asyncio

async def latido():
    for numero in range(3):
        await asyncio.sleep(0.5)
        print(f"latido {numero}")

async def main():
    tarea = asyncio.create_task(latido())

    print("La coroutine principal continúa")
    await asyncio.sleep(0.75)

    await tarea

asyncio.run(main())

Conserva una referencia y espera la tarea. Crear tareas sin propietario puede ocultar excepciones o permitir que el programa termine antes de completarlas.

TaskGroup y concurrencia estructurada

import asyncio

async def trabajador(nombre, demora):
    await asyncio.sleep(demora)
    print(f"{nombre} terminado")

async def main():
    async with asyncio.TaskGroup() as grupo:
        grupo.create_task(trabajador("A", 0.4))
        grupo.create_task(trabajador("B", 0.2))
        grupo.create_task(trabajador("C", 0.6))

asyncio.run(main())

El context manager espera todas las tareas. Si una falla, cancela las relacionadas y comunica los errores de forma agrupada.

Aplicar timeouts

import asyncio

async def operacion_lenta():
    await asyncio.sleep(5)
    return "terminada"

async def main():
    try:
        async with asyncio.timeout(1):
            resultado = await operacion_lenta()
            print(resultado)
    except TimeoutError:
        print("La operación superó el tiempo límite")

asyncio.run(main())

Un timeout impide esperar indefinidamente. Los errores deben manejarse de forma específica, como se explica en la guía de try y except en Python.

Cancelación y limpieza

import asyncio

async def monitor():
    try:
        while True:
            print("comprobando")
            await asyncio.sleep(1)
    finally:
        print("limpieza del monitor")

async def main():
    tarea = asyncio.create_task(monitor())
    await asyncio.sleep(2.2)

    tarea.cancel()

    try:
        await tarea
    except asyncio.CancelledError:
        print("monitor cancelado")

asyncio.run(main())

La cancelación es un evento normal en programas asíncronos. Utiliza finally para liberar recursos y no ocultes CancelledError sin una razón clara.

Proteger estado compartido

import asyncio

saldo = 0
bloqueo = asyncio.Lock()

async def agregar(cantidad):
    global saldo

    async with bloqueo:
        actual = saldo
        await asyncio.sleep(0)
        saldo = actual + cantidad

async def main():
    await asyncio.gather(*(agregar(1) for _ in range(100)))
    print(saldo)

asyncio.run(main())

Las coroutines pueden sufrir condiciones de carrera cuando leen un valor, esperan y después escriben basándose en un estado antiguo.

Usar una cola asíncrona

import asyncio

async def productor(cola):
    for elemento in range(5):
        await cola.put(elemento)
    await cola.put(None)

async def consumidor(cola):
    while True:
        elemento = await cola.get()
        try:
            if elemento is None:
                return
            print(f"procesado {elemento}")
        finally:
            cola.task_done()

async def main():
    cola = asyncio.Queue(maxsize=2)
    await asyncio.gather(productor(cola), consumidor(cola))

asyncio.run(main())

El valor None funciona aquí como señal de finalización. Consulta la guía de None en Python para comprender este uso.

No bloquear el event loop

Las llamadas síncronas lentas detienen todo el bucle. Cuando no existe una alternativa asíncrona, puedes mover trabajo bloqueante a un hilo.

import asyncio
from pathlib import Path

def leer_archivo_grande():
    return Path("informe.txt").read_text(encoding="utf-8")

async def main():
    texto = await asyncio.to_thread(leer_archivo_grande)
    print(len(texto))

asyncio.run(main())

La guía de pathlib en Python muestra operaciones modernas con archivos y rutas.

Registrar errores y estado

En aplicaciones reales, utiliza el módulo de logging en Python en lugar de depender de print(). Registra identificadores y contexto útil, pero evita credenciales y datos sensibles.

Probar código asíncrono

Las funciones pequeñas y la separación de dependencias facilitan las pruebas. La guía de Pytest para principiantes introduce assertions, excepciones y organización de suites.

Cuándo asyncio es adecuado

  • Muchas solicitudes de red o conexiones de larga duración.
  • Servicios y clientes con alta concurrencia.
  • Pipelines con colas, temporizadores y subprocesses asíncronos.
  • Aplicaciones que ya utilizan frameworks y drivers compatibles.
  • Cargas dominadas por espera, no por cálculo.

Cuándo no utilizarlo

  • Un script secuencial pequeño.
  • Cálculo intensivo que necesita multiprocessing o librerías vectorizadas.
  • Dependencias completamente bloqueantes que no pueden aislarse.
  • Proyectos donde la complejidad adicional no aporta una mejora real.

Conclusión

asyncio en Python es un sistema de cooperación para trabajo orientado a entrada y salida. Mantén un único punto de entrada con asyncio.run(), espera las tareas que creas, utiliza TaskGroup, establece timeouts, conserva la cancelación y evita llamadas bloqueantes dentro del event loop.

Documentación oficial

La documentación oficial de Python presenta una visión general de asyncio y una referencia detallada de coroutines, tareas, TaskGroup, cancelación y timeouts.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Detailed view of a resting reticulated python showcasing its textured scales and intricate patterns.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TaskGroup en Python: concurrencia estructurada

    Aprende asyncio.TaskGroup en Python para concurrencia estructurada, resultados, cancelación, ExceptionGroup, timeouts y grupos anidados.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A monochrome image of a lens on an open dictionary page, highlighting words.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    MappingProxyType: diccionario solo lectura

    Aprende MappingProxyType en Python para exponer diccionarios de solo lectura, crear vistas dinámicas y snapshots, y proteger invariantes sin copias.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Retro fuel pump with rusty metal and vintage design, featuring a nozzle and liter meter.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Literal en Python: restringe valores

    Aprende typing.Literal en Python para restringir valores, crear overloads, discriminar TypedDict, usar match/case y mejorar APIs tipadas.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TypedDict en Python: diccionarios tipados

    Aprende TypedDict en Python para diccionarios tipados, claves opcionales, NotRequired, Required, payloads de APIs y variantes discriminadas.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Protocol en Python: tipado estructural

    Aprende Python Protocol para tipado estructural, contratos genéricos, callbacks, runtime_checkable, pruebas e inyección de dependencias.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview en Python: buffers sin copia

    Aprende memoryview en Python para buffers sin copia, slices, bytearray editable, cast, mmap, struct, sockets y control seguro del ciclo

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026