Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The await foreach statement is an asynchronous iteration construct introduced in C# 8.0 that allows developers to consume data streams asynchronously. It sequentially processes elements as they become available, yielding the thread execution context back to the caller between iterations rather than blocking while waiting for the next element to materialize.
await foreach (T variable in asyncEnumerableExpression)
{
    // Execution body
}

Architectural Mechanics

Standard synchronous foreach loops operate on IEnumerable<T> and block the executing thread during IEnumerator<T>.MoveNext(). Conversely, await foreach operates on asynchronous streams. The critical distinction lies in the enumerator: MoveNextAsync() returns a ValueTask<bool> instead of a synchronous bool. When the compiler encounters an await foreach loop, it lowers the syntax into a state machine that handles asynchronous advancement and, if applicable, asynchronous disposal. The conceptual compiler expansion of an await foreach loop looks like this:
var enumerator = asyncEnumerableExpression.GetAsyncEnumerator();
try
{
    // Awaits the asynchronous retrieval of the next element
    while (await enumerator.MoveNextAsync()) 
    {
        T variable = enumerator.Current;
        // Execution body
    }
}
finally
{
    // Asynchronously disposes of resources only if the enumerator supports it
    if (enumerator is IAsyncDisposable asyncDisposable)
    {
        await asyncDisposable.DisposeAsync(); 
    }
}

Cancellation Integration

Because asynchronous operations often require cancellation mechanisms, await foreach supports CancellationToken injection. However, you cannot pass the token directly into the loop syntax. Instead, you must use the WithCancellation() extension method provided by the System.Threading.Tasks namespace. This method binds the CancellationToken to the underlying enumerator, ensuring that the token is passed to the producer’s asynchronous state machine.
CancellationTokenSource cts = new CancellationTokenSource();

await foreach (var item in asyncEnumerableExpression.WithCancellation(cts.Token))
{
    // Execution body
}

Context Synchronization

By default, await foreach captures the current SynchronizationContext (if one exists) and attempts to marshal the continuation back to that context after every awaited MoveNextAsync() call. To prevent this overhead and force continuations to execute on a thread pool thread, you must append ConfigureAwait(false). When combining ConfigureAwait and WithCancellation, the order of method chaining does not matter, but both must be applied directly to the enumerable expression.
await foreach (var item in asyncEnumerableExpression
    .WithCancellation(cancellationToken)
    .ConfigureAwait(false))
{
    // Execution body
}

Interface and Pattern Requirements

For a type to be compatible with await foreach, it must satisfy one of the following conditions:
  1. Implement the IAsyncEnumerable<T> interface.
  2. Satisfy the asynchronous enumerable pattern (duck typing) by exposing a GetAsyncEnumerator() method that is callable with no arguments (meaning it can be parameterless, or all of its parameters can be optional). This method can be implemented as an instance method or as an extension method, which allows developers to add await foreach support to external or third-party types.
The enumerator type returned by GetAsyncEnumerator() must satisfy the asynchronous enumerator pattern by providing:
  • A MoveNextAsync() method that returns a ValueTask<bool> (or any awaitable type yielding a bool).
  • A Current property exposing the current element.
Implementing DisposeAsync() (or the IAsyncDisposable interface) on the enumerator is entirely optional. If the disposal method is absent, the compiler simply skips the disposal step during the lowering process without throwing an error.
Master C# with Deep Grasping Methodology!Learn More