TIL: IAsyncEnumerable en .NET — cancelación y backpressure sin sustos
Aprendizaje práctico sobre cómo consumir IAsyncEnumerable de forma segura en APIs y workers, controlando cancelación y evitando cuellos de botella silenciosos.
TL;DR
IAsyncEnumerable es potente, pero si no propagas CancellationToken y controlas el ritmo de consumo, puedes generar trabajo inútil y picos de memoria.
Índice
Hoy me he encontrado con un problema clásico: stream asíncrono correcto en local, comportamiento errático en carga real.
La causa no era el algoritmo, sino dos detalles de uso de IAsyncEnumerable<T>.
1) Propaga siempre el CancellationToken
await foreach (var item in source.WithCancellation(ct))
{
await ProcessAsync(item, ct);
}
Sin WithCancellation(ct), cuando el cliente cancela la petición el productor puede seguir generando elementos un rato.
Eso se traduce en trabajo inútil y consumo innecesario.
2) Controla el ritmo de consumo
IAsyncEnumerable no es magia contra backpressure.
Si cada item dispara IO costoso en paralelo sin límite, saturas recursos rápidamente.
Patrón seguro para fan-out moderado:
- buffer acotado,
- grado de paralelismo explícito,
- timeouts por operación externa.
Regla práctica
- Flujo secuencial:
await foreachdirecto. - Flujo paralelo: límite de concurrencia claro (por ejemplo,
SemaphoreSlim). - Llamadas externas: timeout + cancelación enlazada.
TL;DR
IAsyncEnumerable mejora latencia percibida y uso de memoria cuando se aplica bien.
Pero en producción, cancelación y control de ritmo son obligatorios para evitar comportamiento frágil.