0Pricing
C# Academy · Lección

IAsyncEnumerable y await foreach

Transmita datos de forma asíncrona con IAsyncEnumerable, use yield en iteradores asíncronos y consúmalos con await foreach.

IAsyncEnumerable y await foreach es una lección gratuita de C# Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C# Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C# Academy incluye 4 lecciones en total.

¿Por qué IAsyncEnumerable?

IAsyncEnumerable<T> (introducido en C# 8 y .NET Core 3) permite el streaming asíncrono: produce los elementos de uno en uno a medida que están disponibles, sin cargar primero todo en memoria. Piense en leer fila por fila un cursor de base de datos o en transmitir respuestas de una API.

Escribir un iterador asíncrono

Declare un método que devuelva IAsyncEnumerable<T> y use yield return dentro de un método asíncrono. Las palabras clave await y yield return coexisten de forma natural.

public async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
    for (int i = 0; i < count; i++)
    {
        await Task.Delay(100); // simulate async work per item
        yield return i;
    }
}

Consumir con await foreach

Consuma un IAsyncEnumerable<T> con await foreach. Cada iteración espera de forma asíncrona el siguiente elemento; el consumidor y el productor avanzan de forma sincronizada.

await foreach (var number in GenerateNumbersAsync(10))
{
    Console.WriteLine(number);
}

// With cancellation:
var cts = new CancellationTokenSource();
await foreach (var n in GenerateNumbersAsync(100)
    .WithCancellation(cts.Token))
{
    if (n > 50) { cts.Cancel(); break; }
    Console.WriteLine(n);
}

Transmitir datos desde una base de datos

EF Core admite la transmisión de resultados de consultas con AsAsyncEnumerable(). Esto lee las filas de una en una sin cargar todo el conjunto de resultados en memoria, algo fundamental para tablas grandes.

public async IAsyncEnumerable<Product> StreamProductsAsync(
    [EnumeratorCancellation] CancellationToken ct = default)
{
    await foreach (var product in _db.Products
        .Where(p => p.IsActive)
        .AsAsyncEnumerable()
        .WithCancellation(ct))
    {
        yield return product;
    }
}

Transmitir respuestas HTTP

Transmita respuestas grandes a los clientes sin almacenar en búfer toda la carga útil. El cliente recibe los datos progresivamente mientras el servidor continúa procesándolos.

app.MapGet("/stream/products", (ProductRepository repo) =>
{
    // Returns an IAsyncEnumerable — ASP.NET Core streams it as JSON array
    return repo.StreamAllAsync();
});

// Or with custom serialization:
app.MapGet("/stream/events", async (HttpResponse response, EventStore store) =>
{
    response.ContentType = "application/json";
    await foreach (var e in store.StreamAsync())
    {
        await response.WriteAsJsonAsync(e);
        await response.Body.FlushAsync();
    }
});

El atributo EnumeratorCancellation

Aplique [EnumeratorCancellation] al parámetro CancellationToken de un iterador asíncrono para que WithCancellation(), usado en el consumidor, transfiera el token al productor.

public async IAsyncEnumerable<LogEntry> TailLogAsync(
    string logFile,
    [EnumeratorCancellation] CancellationToken ct = default)
{
    while (!ct.IsCancellationRequested)
    {
        var newLines = await ReadNewLinesAsync(logFile, ct);
        foreach (var line in newLines)
            yield return new LogEntry(line);
        await Task.Delay(500, ct);
    }
}

// Consumer:
await foreach (var entry in TailLogAsync("/var/log/app.log")
    .WithCancellation(cts.Token))
{
    Console.WriteLine(entry.Message);
}

LINQ sobre IAsyncEnumerable

El paquete NuGet System.Linq.Async proporciona operadores LINQ asíncronos (WhereAwait, SelectAwait, ToListAsync) para IAsyncEnumerable.

// dotnet add package System.Linq.Async

var expensiveProducts = await repo.StreamAllAsync()
    .WhereAwait(async p => await pricing.IsExpensiveAsync(p.Id))
    .SelectAwait(async p => await enricher.EnrichAsync(p))
    .Take(10)
    .ToListAsync();

// Or use built-in Where/Select that don't need async:
var names = repo.StreamAllAsync()
    .Where(p => p.IsActive)
    .Select(p => p.Name);

Combinar varios streams asíncronos

Para procesar varios streams asíncronos de forma simultánea, use Channel como punto de concentración: escriba desde varios productores y lea desde un único consumidor.

async IAsyncEnumerable<T> Merge<T>(
    params IAsyncEnumerable<T>[] streams)
{
    var channel = Channel.CreateUnbounded<T>();

    var producers = streams.Select(async stream =>
    {
        await foreach (var item in stream)
            await channel.Writer.WriteAsync(item);
    });

    _ = Task.WhenAll(producers)
            .ContinueWith(_ => channel.Writer.Complete());

    await foreach (var item in channel.Reader.ReadAllAsync())
        yield return item;
}

Rendimiento: almacenamiento en búfer frente a streaming

Transmita siempre los datos grandes mientras los procesa. Cargarlo todo primero en una lista (ToListAsync()) mantiene todos los datos en memoria simultáneamente; el streaming procesa un elemento cada vez.

// BAD: loads all 1M records into memory
var allOrders = await _db.Orders.ToListAsync();
foreach (var order in allOrders)
    await ProcessAsync(order);

// GOOD: processes one record at a time
await foreach (var order in _db.Orders.AsAsyncEnumerable())
    await ProcessAsync(order);
// Memory usage: O(1) vs O(n)

Gestión de errores en iteradores asíncronos

Envuelva todo el await foreach en un try/catch para gestionar los errores del productor. Los errores del iterador se propagan al consumidor en el punto de la iteración actual.

try
{
    await foreach (var item in RiskyStreamAsync())
    {
        await ProcessAsync(item);
    }
}
catch (HttpRequestException ex)
{
    Console.Error.WriteLine($"Stream error: {ex.Message}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Stream cancelled.");
}

Comprobación rápida

¿Cuál es la principal ventaja de IAsyncEnumerable en cuanto al uso de memoria frente a devolver una List<T>?

Resumen: IAsyncEnumerable y await foreach

Conclusiones clave:

  • IAsyncEnumerable<T>: transmite elementos de forma asíncrona, uno cada vez
  • yield return en métodos asíncronos produce iteradores asíncronos
  • await foreach: consume el stream con cancelación mediante .WithCancellation()
  • [EnumeratorCancellation]: propaga el token del consumidor al iterador
  • EF Core: use AsAsyncEnumerable() para realizar consultas grandes con un uso eficiente de la memoria
  • Para procesar datos grandes, transmítalos en lugar de usar ToListAsync()

Preguntas frecuentes

¿La lección «IAsyncEnumerable y await foreach» es gratis?

Sí — el texto completo de «IAsyncEnumerable y await foreach» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C# Academy, actualiza a CoddyKit PRO. El curso de C# Academy incluye 4 lecciones en total.

¿Qué aprenderé en «IAsyncEnumerable y await foreach»?

Transmita datos de forma asíncrona con IAsyncEnumerable, use yield en iteradores asíncronos y consúmalos con await foreach. Practicas C# Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar C# Academy?

No se requiere experiencia previa. C# Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.

¿Cuánto tiempo toma la lección «IAsyncEnumerable y await foreach»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de C# Academy?

Sí. Cada lección de C# Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. IAsyncEnumerable y await foreach
  2. System.Threading.Channels
  3. ValueTask y cómo evitar asignaciones
  4. ConfigureAwait y contexto de sincronización
← Volver a C# Academy