Loading...

Mastering Asynchronous Streams with IAsyncEnumerable in C#

Mastering Asynchronous Streams with IAsyncEnumerable in C#

In modern C# development, handling asynchronous data streams efficiently is crucial. The introduction of IAsyncEnumerable and await foreach in C# 8.0 (and enhanced in .NET 8) provides a powerful and elegant way to consume and produce asynchronous sequences of data.

The Problem with Traditional Async

Before IAsyncEnumerable, if you had an asynchronous operation that returned multiple items over time, you would typically collect all items into a List and then return the complete list as a Task

  • >. This approach has two main drawbacks:

    • Latency: You have to wait for all items to be produced before you can start processing any of them.
    • Memory Consumption: All items must be held in memory simultaneously, which can be problematic for large datasets.

    Introducing IAsyncEnumerable

    IAsyncEnumerable allows you to asynchronously iterate over a sequence of data, yielding items one by one as they become available. This combines the benefits of asynchronous programming with the efficiency of streaming data.

    Producing an Async Stream:

    public static async IAsyncEnumerable GenerateNumbersAsync() { for (int i = 0; i < 10; i++) { await Task.Delay(100); // Simulate async work yield return i; } }

    Consuming an Async Stream with await foreach:

    public static async Task ConsumeNumbersAsync() { await foreach (var number in GenerateNumbersAsync()) { Console.WriteLine($"Received: {number}"); } } // To run this: // await ConsumeNumbersAsync();

    Error Handling and Cancellation

    Error handling works as expected with try-catch blocks. For cancellation, you can pass a CancellationToken to your IAsyncEnumerable producer and consumer methods.

    public static async IAsyncEnumerable GenerateCancellableNumbersAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { for (int i = 0; i < 10; i++) { cancellationToken.ThrowIfCancellationRequested(); await Task.Delay(100, cancellationToken); yield return i; } } public static async Task ConsumeCancellableNumbersAsync() { using var cts = new CancellationTokenSource(500); // Cancel after 500ms try { await foreach (var number in GenerateCancellableNumbersAsync(cts.Token)) { Console.WriteLine($"Received: {number}"); } } catch (OperationCanceledException) { Console.WriteLine("Operation cancelled!"); } }

    Conclusion

    IAsyncEnumerable is a powerful addition to C# for handling asynchronous data streams, enabling more responsive and memory-efficient applications. It's particularly useful for scenarios like reading from databases, file streams, or network APIs where data arrives incrementally.

  • Comments

    Leave a comment

    No comments yet. Be the first to share your thoughts!