C# Async Streams
An async stream produces values one at a time with pauses between them. The IAsyncEnumerable<T> interface describes such a stream, and await foreach reads it.
A Counting Stream
The Count method yields the numbers 1 to 3. Main reads the stream with await foreach.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async IAsyncEnumerable<int> Count()
{
for (int i = 1; i <= 3; i++)
{
await Task.CompletedTask;
yield return i;
}
}
static async Task Main(string[] args)
{
await foreach (int n in Count())
{
Console.WriteLine("Got " + n);
}
}
}Only the Long Names
The stream checks each name before it yields. Only names longer than two letters reach Main.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async IAsyncEnumerable<string> LongNames()
{
string[] names = { "Al", "Hana", "Bo", "Mira" };
foreach (string name in names)
{
await Task.CompletedTask;
if (name.Length > 2)
{
yield return name;
}
}
}
static async Task Main(string[] args)
{
await foreach (string name in LongNames())
{
Console.WriteLine(name);
}
}
}Breaking Out Early
The iterator prints a line as it makes each value. Main breaks after the second value, so the third value is never made.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async IAsyncEnumerable<int> Produce()
{
for (int i = 1; i <= 3; i++)
{
await Task.CompletedTask;
Console.WriteLine("Made " + i);
yield return i;
}
}
static async Task Main(string[] args)
{
await foreach (int n in Produce())
{
Console.WriteLine("Got " + n);
if (n == 2)
{
break;
}
}
Console.WriteLine("Stopped");
}
}Example
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static async IAsyncEnumerable<int> Squares()
{
for (int i = 1; i <= 4; i++)
{
await Task.CompletedTask;
yield return i * i;
}
}
static async Task Main(string[] args)
{
await foreach (int square in Squares())
{
Console.WriteLine(square);
}
}
}Key points
- An async iterator is an async method that uses yield return to produce values.
- Each yield return hands one value to the reader before the method continues.
- The method can await between values, so production can pause.
- await foreach reads the stream one value at a time, in stream order.
- A break inside await foreach stops the stream early.
- System.Collections.Generic provides the IAsyncEnumerable<T> interface.
In this example, Squares yields the square of each number from 1 to 4. The method awaits before each yield. Main prints each square as the stream produces it.
Note
The stream ends when the iterator method finishes, and the reader loop ends with it.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does await foreach do with an async stream?