IAsyncEnumerable และ await foreach
สตรีมข้อมูลแบบอะซิงโครนัสด้วย IAsyncEnumerable ใช้ yield ในตัววนซ้ำแบบอะซิงโครนัส และรับข้อมูลด้วย await foreach
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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- IAsyncEnumerable และ await foreach
- System.Threading.Channels
- ValueTask และการหลีกเลี่ยงการจัดสรรหน่วยความจำ
- ConfigureAwait และบริบทการซิงโครไนซ์