0Pricing
C# Academy · Aula

IAsyncEnumerable e await foreach

Transmita dados de forma assíncrona com IAsyncEnumerable, use yield em iteradores assíncronos e consuma-os com await foreach.

IAsyncEnumerable e await foreach é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

Por que usar IAsyncEnumerable?

IAsyncEnumerable<T> (introduzido no C# 8 e no .NET Core 3) permite transmissão assíncrona — você produz os itens um por vez, à medida que ficam disponíveis, sem carregar tudo na memória primeiro. Pense em ler as linhas de um cursor de banco de dados uma a uma ou transmitir respostas de API.

Escrevendo um Iterador Assíncrono

Declare um método que retorne IAsyncEnumerable<T> e use yield return dentro de um método assíncrono. As palavras-chave await e yield return coexistem naturalmente.

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;
    }
}

Consumindo com await foreach

Consuma um IAsyncEnumerable<T> com await foreach. Cada iteração aguarda o próximo item de forma assíncrona — o consumidor e o produtor avançam naturalmente no mesmo ritmo.

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);
}

Transmitindo Dados de um Banco de Dados

O EF Core oferece suporte à transmissão de resultados de consultas com AsAsyncEnumerable(). Isso lê as linhas uma por vez, sem carregar todo o conjunto de resultados na memória — algo essencial para tabelas 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;
    }
}

Transmitindo Respostas HTTP

Transmita respostas grandes para os clientes sem armazenar toda a carga útil em um buffer. O cliente recebe os dados progressivamente enquanto o servidor continua o processamento.

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();
    }
});

O Atributo EnumeratorCancellation

Aplique [EnumeratorCancellation] ao parâmetro CancellationToken de um iterador assíncrono para que WithCancellation(), no lado do consumidor, encaminhe o token ao produtor.

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 em IAsyncEnumerable

O pacote NuGet System.Linq.Async fornece operadores LINQ assí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);

Mesclando Vários Fluxos Assíncronos

Para processar vários fluxos assíncronos simultaneamente, use Channel como ponto de convergência — escreva a partir de vários produtores e leia de um ú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;
}

Desempenho: Armazenamento em Buffer vs. Transmissão

Transmita sempre ao processar grandes conjuntos de dados. Carregar tudo primeiro em uma lista (ToListAsync()) mantém todos os dados na memória simultaneamente — a transmissão processa um item por 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)

Tratamento de Erros em Iteradores Assíncronos

Envolva todo o await foreach em um try/catch para tratar erros do produtor. Os erros no iterador são propagados ao consumidor no ponto da iteração atual.

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.");
}

Verificação Rápida

Qual é a principal vantagem de memória de IAsyncEnumerable em comparação com retornar um List<T>?

Recapitulação: IAsyncEnumerable e await foreach

Principais conclusões:

  • IAsyncEnumerable<T>: transmite itens de forma assíncrona, um por vez
  • yield return em métodos assíncronos produz iteradores assíncronos
  • await foreach: consome o fluxo com cancelamento por meio de .WithCancellation()
  • [EnumeratorCancellation]: propaga o token do consumidor para o iterador
  • EF Core: use AsAsyncEnumerable() para consultas grandes com uso eficiente da memória
  • Ao processar grandes conjuntos de dados, transmita em vez de usar ToListAsync()

Perguntas Frequentes

A aula “IAsyncEnumerable e await foreach” é grátis?

Sim — o texto completo de “IAsyncEnumerable e await foreach” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “IAsyncEnumerable e await foreach”?

Transmita dados de forma assíncrona com IAsyncEnumerable, use yield em iteradores assíncronos e consuma-os com await foreach. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “IAsyncEnumerable e await foreach”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. IAsyncEnumerable e await foreach
  2. System.Threading.Channels
  3. ValueTask e como evitar alocações
  4. ConfigureAwait e contexto de sincronização
← Voltar para C# Academy