tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

yield return await and await foreach geekery

29 Jan 2019 C#, Roslyn

This is just a pure geekery, but as I was playing with asynchronous enumerables I realized I can combine the await and yield return together. In fact, I can even throw await foreach into the mix.

Thus, this code (completely useless) is compiled without the compiler complaining.

async IAsyncEnumerable<int> FooBarAsync()
{
	await foreach (var n in FooBarAsync())
	{
		yield return await Task.FromResult(n);
	}
}

Why is this so interesting that I’m writing about it? Well, thinking about what’s going on behind makes me giggle a bit (I know it’s just a machine producing code, but still…geek).

Let’s start from the inside. The await produces one state machine, because that’s how the couroutine is internally represented. Then the yield return which is the same story. And finally, the await foreach that works on the MoveNextAsync of the async enumerator. To take it from other direction, the code calls top-level FooBarAsync and hits the first iteration of await foreach, that asynchronously waits for the MoveNextAsync to complete and goes to produce the result in yield return, which the caller is going to consume. But before that happens the await after the yield return needs to complete. Only now the caller can continue.

Also having yield return await, three keywords, together is not that usual (although one can come up with various other ways to have three (or more) keywords, declarations excluded, together).

Profile Picture Jiří Činčura is an independent developer focusing on data and business layers, language constructs, parallelism and databases. Specifically Entity Framework, asynchronous and parallel programming, cloud and Azure. He's Microsoft Most Valuable Professional and you can read his articles, guides, tips and tricks at www.tabsoverspaces.com.