Can I await an enumerable I create with a generator?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In C#, you cannot await a normal IEnumerable produced by an iterator method. await works on awaitable values such as Task and ValueTask, while a synchronous enumerable is just a pull-based sequence of values.
Understand the Type You Actually Have
A generator-style iterator in C# usually means yield return, which produces IEnumerable<T> or IAsyncEnumerable<T> depending on how it is written.
A normal synchronous iterator:
You consume that with foreach, not await:
await Numbers() does not make sense because IEnumerable<int> is not an awaitable operation.
Use Task.WhenAll If the Enumerable Contains Tasks
Sometimes the enumerable itself is synchronous, but it contains asynchronous operations:
In that case, you do not await the enumerable. You await the tasks it contains:
That is a very different pattern from awaiting the sequence object itself.
This distinction is easy to miss because both patterns involve "many asynchronous things". But the shape of the types matters:
- '
IEnumerable<Task<T>>means a normal sequence of asynchronous operations' - '
IAsyncEnumerable<T>means an asynchronously produced sequence of values'
Use IAsyncEnumerable<T> for Asynchronous Iteration
If the generator itself is asynchronous over time, use async iterators and consume them with await foreach.
Consume it like this:
This is the C# feature that most closely matches "an awaitable sequence from a generator".
It is also the right tool when values arrive over time and you want to process them one by one instead of waiting for every result up front.
Materialize an Async Sequence When You Need a Final Result
Sometimes you do want a single awaited result, but the correct shape is "consume the async sequence, then return a collection". In that case, iterate with await foreach and build the result yourself:
This pattern makes the control flow explicit. You are not awaiting the sequence object itself. You are awaiting each asynchronous move to the next item until the sequence is exhausted, then returning a regular List<int> wrapped in a Task<List<int>>.
Common Pitfalls
The biggest mistake is trying to use await on IEnumerable<T> directly. A sequence is not the same thing as an asynchronous operation.
Another common issue is confusing "enumerable of tasks" with "async enumerable". IEnumerable<Task<T>> means the sequence is synchronous but each element is asynchronous. IAsyncEnumerable<T> means the sequence itself is asynchronous to iterate.
People also forget that await foreach requires IAsyncEnumerable<T>, not just any collection with delayed work inside it.
Finally, if you only need all asynchronous results at once, Task.WhenAll is often simpler than creating an async iterator.
Summary
- You cannot
awaita normalIEnumerable<T>. - If the enumerable yields tasks, await those tasks with something like
Task.WhenAll. - If the sequence itself is asynchronous, use
IAsyncEnumerable<T>andawait foreach. - Distinguish clearly between synchronous sequences and asynchronous iteration.
- Choose the pattern based on whether the asynchrony belongs to the items or to the sequence itself.

