C# Academy · บทเรียน

IAsyncEnumerable และ await foreach

สตรีมข้อมูลแบบอะซิงโครนัสด้วย IAsyncEnumerable ใช้ yield ในตัววนซ้ำแบบอะซิงโครนัส และรับข้อมูลด้วย await foreach

บทเรียน 1 จาก 412 ขั้นตอน

IAsyncEnumerable และ await foreach เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดจึงต้องใช้ IAsyncEnumerable

IAsyncEnumerable<T> (เริ่มใช้ใน C# 8 และ .NET Core 3) ทำให้เกิดการสตรีมแบบอะซิงโครนัส — คุณส่งรายการออกมาทีละรายการเมื่อรายการนั้นพร้อมใช้งาน โดยไม่ต้องโหลดทุกอย่างเข้าสู่หน่วยความจำก่อน ลองนึกถึงการอ่านแถวจากเคอร์เซอร์ฐานข้อมูลทีละแถว หรือการสตรีมการตอบกลับจาก API

การเขียนตัววนซ้ำแบบอะซิงโครนัส

ประกาศเมธอดที่ส่งคืนค่าเป็น IAsyncEnumerable<T> และใช้ yield return ภายในเมธอดแบบอะซิงโครนัส คำสำคัญ await และ yield return สามารถใช้ร่วมกันได้อย่างเป็นธรรมชาติ

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

การใช้ await foreach

ใช้ IAsyncEnumerable<T> ด้วย await foreach ในแต่ละรอบ โปรแกรมจะรอรายการถัดไปแบบอะซิงโครนัส ทำให้ผู้บริโภคและผู้ผลิตดำเนินไปตามจังหวะที่เหมาะสมโดยธรรมชาติ

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

การสตรีมจากฐานข้อมูล

EF Core รองรับการสตรีมผลลัพธ์การสืบค้นด้วย AsAsyncEnumerable() วิธีนี้จะอ่านแถวทีละแถวโดยไม่โหลดชุดผลลัพธ์ทั้งหมดเข้าสู่หน่วยความจำ ซึ่งสำคัญอย่างยิ่งสำหรับตารางขนาดใหญ่

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

การสตรีมการตอบกลับ HTTP

สตรีมการตอบกลับขนาดใหญ่ไปยังไคลเอ็นต์โดยไม่เก็บข้อมูลทั้งหมดไว้ในบัฟเฟอร์ ไคลเอ็นต์จะได้รับข้อมูลอย่างต่อเนื่องขณะที่เซิร์ฟเวอร์ยังคงประมวลผลต่อไป

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

แอตทริบิวต์ EnumeratorCancellation

ใช้ [EnumeratorCancellation] กับพารามิเตอร์ CancellationToken ของตัววนซ้ำแบบอะซิงโครนัส เพื่อให้ WithCancellation() ทางฝั่งผู้บริโภคส่งโทเค็นต่อไปยังผู้ผลิต

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 สำหรับ IAsyncEnumerable

แพ็กเกจ NuGet System.Linq.Async มีตัวดำเนินการ LINQ แบบอะซิงโครนัส (WhereAwait, SelectAwait, ToListAsync) สำหรับ 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);

การรวมสตรีมแบบอะซิงโครนัสหลายรายการ

หากต้องการประมวลผลสตรีมแบบอะซิงโครนัสหลายรายการพร้อมกัน ให้ใช้ Channel เป็นจุดรวมข้อมูล — เขียนข้อมูลจากผู้ผลิตหลายรายและอ่านจากผู้บริโภครายเดียว

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

ประสิทธิภาพ: การเก็บในบัฟเฟอร์เทียบกับการสตรีม

ควรสตรีมเสมอเมื่อประมวลผลชุดข้อมูลขนาดใหญ่ การโหลดทุกอย่างลงในรายการก่อนด้วย ToListAsync() จะเก็บข้อมูลทั้งหมดไว้ในหน่วยความจำพร้อมกัน ส่วนการสตรีมจะประมวลผลทีละรายการ

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

การจัดการข้อผิดพลาดในตัววนซ้ำแบบอะซิงโครนัส

ครอบ await foreach ทั้งหมดไว้ใน try/catch เพื่อจัดการข้อผิดพลาดจากผู้ผลิต ข้อผิดพลาดในตัววนซ้ำจะส่งต่อไปยังผู้บริโภค ณ จุดที่กำลังวนซ้ำอยู่

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

ตรวจสอบความเข้าใจ

ข้อได้เปรียบหลักด้านหน่วยความจำของ IAsyncEnumerable เมื่อเทียบกับการส่งคืน List<T> คืออะไร

สรุป: IAsyncEnumerable และ await foreach

ประเด็นสำคัญ:

  • IAsyncEnumerable<T>: สตรีมรายการแบบอะซิงโครนัสทีละรายการ
  • yield return ในเมธอดแบบอะซิงโครนัสจะสร้างตัววนซ้ำแบบอะซิงโครนัส
  • await foreach: ใช้สตรีมพร้อมการยกเลิกผ่าน .WithCancellation()
  • [EnumeratorCancellation]: ส่งโทเค็นของผู้บริโภคต่อไปยังตัววนซ้ำ
  • EF Core: ใช้ AsAsyncEnumerable() สำหรับการสืบค้นขนาดใหญ่ที่ใช้หน่วยความจำอย่างมีประสิทธิภาพ
  • การประมวลผลชุดข้อมูลขนาดใหญ่: ใช้การสตรีมแทน ToListAsync()
เริ่มต้นได้ฟรี

เรียนรู้ C# ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
93
บทเรียน
346

คำถามที่พบบ่อย

บทเรียน “IAsyncEnumerable และ await foreach” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “IAsyncEnumerable และ await foreach” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “IAsyncEnumerable และ await foreach”

สตรีมข้อมูลแบบอะซิงโครนัสด้วย IAsyncEnumerable ใช้ yield ในตัววนซ้ำแบบอะซิงโครนัส และรับข้อมูลด้วย await foreach คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “IAsyncEnumerable และ await foreach” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม

ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. IAsyncEnumerable และ await foreach
  2. System.Threading.Channels
  3. ValueTask และการหลีกเลี่ยงการจัดสรรหน่วยความจำ
  4. ConfigureAwait และบริบทการซิงโครไนซ์
← กลับไปที่ C# Academy