liveBook · Manning

Await foreach

As explained in C# Concurrency: Asynchronous and multithreaded programming

Await foreach is a C# language feature that asynchronously iterates over elements produced by an asynchronous enumerable.

await foreach asynchronously processes items as they become available, avoiding the need to build an entire collection before iteration. It suits sequences generated or retrieved through asynchronous operations. section 14.0

Understand the compiler-level shape behind the loop:

  • Treat foreach as syntax for GetEnumerator, MoveNext, Current, and disposal; await foreach applies the same shape asynchronously. section 14.1
  • Use IAsyncEnumerable<T>.GetAsyncEnumerator to obtain an IAsyncEnumerator<T>; retrieve each item with MoveNextAsync, which returns ValueTask<bool>. section 14.1
  • Let the compiler combine the yield return and await transformations, generating an asynchronous state machine whose steps advance through MoveNextAsync. section 14.2

Generate and consume delayed values

private async IAsyncEnumerable<int> AsyncYieldDemo() { yield return 1; await Task.Delay(1000); yield return 2; } public async Task UseAsyncYieldDemo() { await foreach (var current in AsyncYieldDemo()) { Console.WriteLine($"Got {current}"); } } section 14.2

Apply practical safeguards:

  • Pass cancellation with collection.WithCancellation(token), and mark an iterator parameter with [EnumeratorCancellation] when the compiler should forward that token. section 14.3
  • Remember that cancellation is only a flag: the iterator must observe the token and stop, typically by passing it to an asynchronous operation. section 14.8
  • Avoid converting asynchronous sequences to blocking IEnumerable<T> unless necessary for non-asynchronous callers; blocking can cause performance problems and deadlocks. section 14.8
Listing 14.2 Async yield return example
private async IAsyncEnumerable<int> AsyncYieldDemo()          #1
{
   yield return 1;
   await Task.Delay(1000);                                    #2
   yield return 2;
}
 
public async Task UseAsyncYieldDemo()
{
   await foreach(var current in AsyncYieldDemo())             #3
   {
         Console.WriteLine($"Got {current}");
   }
}

Inspect a complete async iterator that combines yield return, Task.Delay, and await foreach. section 14.2

How C# Concurrency: Asynchronous and multithreaded programming covers it

How deep it goes
Explains syntax, interfaces, compiler transformations, cancellation, and blocking risks, while deferring broader implementation details.
Examples
Uses an async iterator yielding two integers around Task.Delay, plus cancellation and compiler-generated enumerator listings.
What it assumes
Assumes familiarity with foreach, yield return, async/await, Task, and CancellationToken while contrasting their asynchronous counterparts.
What you can do after
Apply it via C# async iterators to consume asynchronous sequences incrementally and add cooperative cancellation.
How it connects
Connects await foreach with IAsyncEnumerable<T>, IAsyncEnumerator<T>, yield return, cancellation tokens, ConfigureAwait, and asynchronous LINQ.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C# Concurrency: Asynchronous and multithreaded programming. The section links point to the parts of the book it draws on.