C# Academy · Lektion

IAsyncEnumerable og await foreach

Stream data asynkront med IAsyncEnumerable, brug yield i asynkrone iteratorer, og forbrug dem med await foreach.

Lektion 1 af 412 trin

IAsyncEnumerable og await foreach er en gratis C# Academy-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i C# Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. C# Academy-kurset indeholder 4 lektioner i alt.

Hvorfor IAsyncEnumerable?

IAsyncEnumerable<T> (introduceret i C# 8 og .NET Core 3) muliggør asynkron datastrømning – du leverer elementer ét ad gangen, efterhånden som de bliver tilgængelige, uden først at indlæse alt i hukommelsen. Tænk på at læse rækker fra en database én ad gangen eller streame API-svar.

Skriv en asynkron iterator

Deklarér en metode, der returnerer IAsyncEnumerable<T>, og brug yield return inde i en asynkron metode. Nøgleordene await og yield return kan naturligt bruges sammen.

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

Brug med await foreach

Forbrug en IAsyncEnumerable<T> med await foreach. Hver iteration venter asynkront på det næste element – producenten og forbrugeren tilpasser naturligt tempoet til hinanden.

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

Datastrømning fra en database

EF Core understøtter datastrømning af forespørgselsresultater med AsAsyncEnumerable(). Det læser rækker én ad gangen uden at indlæse hele resultatsættet i hukommelsen – afgørende for store tabeller.

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

Datastrømning af HTTP-svar

Stream store svar til klienter uden at gemme hele nyttelasten i en buffer. Klienten modtager data løbende, mens serveren fortsætter behandlingen.

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

Attributten EnumeratorCancellation

Anvend [EnumeratorCancellation] på CancellationToken-parameteren i en asynkron iterator, så WithCancellation() på forbrugersiden fører tokenet videre til producenten.

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

NuGet-pakken System.Linq.Async leverer asynkrone LINQ-operatorer (WhereAwait, SelectAwait, ToListAsync) til 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);

Sammenfletning af flere asynkrone datastrømme

Hvis flere asynkrone datastrømme skal behandles samtidigt, skal du bruge Channel som samlingspunkt – skriv fra flere producenter og læs fra én forbruger.

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

Ydeevne: bufferlagring kontra datastrømning

Brug altid datastrømning ved behandling af store datasæt. Hvis du først indlæser alt i en liste (ToListAsync()), ligger alle data samtidig i hukommelsen – datastrømning behandler ét element ad gangen.

// 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)

Fejlhåndtering i asynkrone iteratorer

Indpak hele await foreach i en try/catch for at håndtere fejl fra producenten. Fejl i iteratoren føres videre til forbrugeren ved den aktuelle iteration.

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

Hurtig kontrol

Hvad er den primære fordel ved IAsyncEnumerable med hensyn til hukommelse sammenlignet med at returnere en List<T>?

Opsummering: IAsyncEnumerable og await foreach

Vigtigste læringspunkter:

  • IAsyncEnumerable<T>: stream elementer asynkront, ét ad gangen
  • yield return i asynkrone metoder producerer asynkrone iteratorer
  • await foreach: forbrug datastrømmen med annullering via .WithCancellation()
  • [EnumeratorCancellation]: fører forbrugerens token videre til iteratoren
  • EF Core: brug AsAsyncEnumerable() til hukommelseseffektive forespørgsler på store datamængder
  • Behandling af store datasæt: brug datastrømning i stedet for ToListAsync()
Gratis at komme i gang

Lær C# med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
93
Lektioner
346

Ofte stillede spørgsmål

Er lektionen “IAsyncEnumerable og await foreach” gratis?

Ja — hele teksten til “IAsyncEnumerable og await foreach” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af C# Academy-kurset, skal du opgradere til CoddyKit PRO. C# Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “IAsyncEnumerable og await foreach”?

Stream data asynkront med IAsyncEnumerable, brug yield i asynkrone iteratorer, og forbrug dem med await foreach. Du øver dig i C# Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på C# Academy?

Der kræves ingen tidligere erfaring. C# Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.

Hvor lang tid tager lektionen “IAsyncEnumerable og await foreach”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne C# Academy-lektion?

Ja. Alle C# Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. IAsyncEnumerable og await foreach
  2. System.Threading.Channels
  3. ValueTask og undgåelse af allokeringer
  4. ConfigureAwait og synkroniseringskontekst
← Tilbage til C# Academy