Course contentsShow
← C#
Lesson 1463 of 1,90813. Real-time Communication with SignalRFree lesson

Streaming Data from Server to Client

Leverage IAsyncEnumerable to push continuous streams of data directly to clients without buffering.

In this lesson, you will learn how to leverage IAsyncEnumerable<T> in SignalR to push continuous streams of data directly to clients without memory-intensive buffering.

The Problem with Traditional Responses

In standard SignalR invocations, a hub method typically returns a single value or completes an action. If you need to send a series of updates—like real-time stock ticks or sensor telemetry—you might be tempted to call Clients.Caller.SendAsync inside a loop. While this works, it lacks backpressure management and tightly couples the hub to the client's connection lifecycle.

Streaming with IAsyncEnumerable

SignalR supports C#'s IAsyncEnumerable<T> natively. When a hub method returns this interface, the SignalR infrastructure handles the heavy lifting: it yields each item as it becomes available and pushes it over the connection stream, rather than waiting for the entire collection to be fully materialized in memory.

Here is how you implement a server-to-client stream:

public class DataHub : Hub
{
    public async IAsyncEnumerable<int> StreamNumbers(int count, [EnumeratorCancellation] CancellationToken ct)
    {
        for (var i = 0; i < count; i++)
        {
            // Simulate work or data arrival
            await Task.Delay(500, ct);
            
            // Yielding pushes the item to the client immediately
            yield return i;
        }
    }
}

Why This Matters

By using IAsyncEnumerable, the server doesn't buffer the result set. If the client is slow, the server effectively "pauses" the enumeration (via the await keyword), preventing the memory bloat that would occur if you were to accumulate all data points before sending. Furthermore, passing a CancellationToken is critical; if the client disconnects or cancels the subscription, the CancellationToken triggers, allowing your server-side loop to exit gracefully and stop processing immediately. This pattern ensures your SignalR application remains responsive and scalable under high-frequency data loads.

Key Takeaway: Using IAsyncEnumerable<T> allows for non-buffered, reactive data streaming that automatically respects client cancellation and optimizes memory usage by yielding items only when the network pipe is ready to receive them.