Take advantage of the ability to create and consume data streams asynchronously in C# 8.0 to improve the performance of your applications. Credit: Vaikoovery Asynchronous programming has been around for quite a while now. The introduction of the async and await keywords in .NET enabled us to write programs that could take advantage of asynchrony with ease. However, there hasn’t been any way to consume streams of data asynchronously until the arrival of IAsyncEnumerable in C# 8.0. IAsyncEnumerable is similar to the IEnumerable method used to iterate over a collection, except that IAsyncEnumerable allows us to move through the collection asynchronously. In other words, IAsyncEnumerable allows us to wait for the next element in the collection without blocking a thread. In this article we’ll take a look at the challenges that IAsyncEnumerable solves, with code examples wherever relevant. To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here. Create a console application project in Visual Studio 2019 First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio 2019. Launch the Visual Studio IDE. Click on “Create new project.” In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed. Click Next. In the “Configure your new project” window shown next, specify the name and location for the new project. Click Create. This will create a new .NET Core console application project in Visual Studio 2019. We’ll use this project to work with IAsyncEnumerable and asynchronous streams in the subsequent sections of this article. Specify the language version in Visual Studio 2019 To be able to work with C# 8.0 in Visual Studio, you must use a project that targets .NET Core, just as we’re doing. You will also need to change the language version of the language in use in your project. To do this, follow the steps outlined below: Right-click on the project. Select “Properties” to invoke the properties window. Click Build -> Advanced. Click on the drop-down control for language version. Select C# 8.0 as the language version. Click OK. IAsyncDisposable, IAsyncEnumerable, and IAsyncEnumerator in C# 8.0 Asynchronous streams enable you to consume streams of data asynchronously. The interfaces IAsyncDisposable, IAsyncEnumerable, and IAsyncEnumerator have been introduced with the release of .NET Standard 2.1. These interfaces enable us to work with asynchronous streams. The following code snippet shows the code of these three interfaces. public interface IAsyncDisposable { ValueTask DisposeAsync(); } public interface IAsyncEnumerable<out T> { IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken token = default); } public interface IAsyncEnumerator<out T> : IAsyncDisposable { ValueTask<bool> MoveNextAsync(); T Current { get; } } Why use asynchronous streams? Imagine that you have a data access library that reads data from a data store and sends back all the results at one go. You can implement such a method easily. All you need to do is make some asynchronous calls to get the data from the underlying data store and then return all of the data at once. This solution is fine as long as you don’t need to return data in pages; in that case, you might have to make several calls to return the data. The best solution in this regard is one in which you can send back the data to the caller as soon as the data is made available. Here is exactly where asynchronous streams come to the rescue. If your method that returns data is synchronous, you could use the yield return statement with IEnumerable. However, this solution would not scale because it would result in a blocking call. The best solution is to use yield return in an asynchronous method that returns IAsyncEnumerable. A method that returns an asynchronous stream returns an instance of IAsyncEnumerable and contains one or more yield return statements. Create an asynchronous stream in C# 8.0 The following code snippet illustrates an asynchronous method that returns Task>. class Program { const int DELAY = 1000; const int MIN = 1; const int MAX = 10; static async Task Main(string[] args) { foreach (int number in await GetData()) { Console.WriteLine(number); } Console.ReadLine(); } static async Task<IEnumerable<int>> GetData() { List<int> integers = new List<int>(); for (int i = MIN; i <= MAX; i++) { await Task.Delay(DELAY); integers.Add(i); } return integers; } } When you execute this application, it will wait for 10 seconds and then display all of the numbers between 1 and 10 at the console window. Although the GetData method is asynchronous, this code will return all of these numbers at once, not one by one as they are generated. This is where the yield keyword comes in. The yield keyword (introduced in C# 2.0) can perform stateful iteration and return each element of a collection one by one. You need not create a temporary collection to store data before it is returned. The following code snippet shows how you can modify the GetData method to incorporate the yield keyword. static async IAsyncEnumerable<int> GetData() { for (int i = MIN; i < MAX; i++) { yield return i; await Task.Delay(DELAY); } } Consume an asynchronous stream in C# 8.0 When consuming an asynchronous stream, you need to specify the await keyword followed by the foreach keyword. You can call the method in the above example from the Main method as shown in the code snippet below. static async Task Main(string[] args) { await foreach (int number in GetData()) { Console.WriteLine(number); } Console.ReadLine(); } And here is the complete program for your reference. using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Test { class Program { const int DELAY = 1000; const int MIN = 1; const int MAX = 10; static async Task Main(string[] args) { await foreach (int number in GetData()) { Console.WriteLine(number); } Console.ReadLine(); } static async IAsyncEnumerable<int> GetData() { for (int i = MIN; i < MAX; i++) { yield return i; await Task.Delay(DELAY); } } } } The support for IAsyncEnumerable (aka asynchronous streams) is one of the most important new features in C# 8.0. You can take advantage of asynchronous streams in your applications to make your code cleaner, more efficient, and high performant. To work with C# 8.0 compiler, you will need Visual Studio 2019 version 16.3 or the .NET Core 3.0 SDK. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos