Reputation: 5569
In a blazor server application, is it okay to send events and call StateHasChanged
very often, e.g., 500 times per second?
One of my pages needs to react to an external event and then update its state accordingly. I found the following solution:
InvokeAsync(() => StateHasChanged())
in the handler.This already works correctly. However, the event may occur very often, e.g., 500 times per second, and I worry about the performance of client and server. Unfortunately, I dont understand which part happens on the server, which part happens on the client, and which data is sent between them.
StateHasChanged
? I think this would impose a high CPU load on the client.Upvotes: 1
Views: 953
Reputation: 51645
It looks that Blazor server can send hundreds of changes by second:
@page "/"
Tics per second: <input type="range" min="1" max="2000" @bind="@CurrentValue" class="slider" id="myRange"> @CurrentValue
<div style="width:500px; height:10px; background-color: blue; position: relative;">
<div class="ball" style="@position_txt"></div>
</div> <br/><br/>
<span>@DateTime.Now.ToString("HH:mm:ss")</span>
<span>Number of renders: @nRenders.ToString("N0")</span>
<button type="button" @onclick="start">start</button>
<style>
.ball {width: 30px; height: 30px; top: -10px;
position: absolute; background-color: blue;}
</style>
@code
{
Int64 nRenders = 0, v = 1, position = 10, CurrentValue = 10;
string position_txt => $"left: {position}px;";
private static System.Timers.Timer aTimer = new System.Timers.Timer();
protected void start()
{
move();
aTimer.Elapsed += (source, e) => move();
aTimer.AutoReset = true;
aTimer.Enabled = !aTimer.Enabled;
}
protected void move()
{
aTimer.Interval = 1000.0/CurrentValue;
position = (position+v);
if (position>500 || position<0) v *= -1;
InvokeAsync(StateHasChanged);
}
protected override void OnAfterRender(bool firstRender) => nRenders++;
}
Upvotes: 3
Reputation: 30001
To answer some of your questions, as your running in Server mode all the real work takes place in the Blazor Hub session.
What a call to StateHasChanged
really does is queue a RenderFragment
onto the Renderer Queue in the Hub Session. Here's the bit of code from ComponentBase
.
_renderFragment = builder =>
{
_hasPendingQueuedRender = false;
_hasNeverRendered = false;
BuildRenderTree(builder);
};
StateHasChanged
looks like this:
protected void StateHasChanged()
{
if (_hasPendingQueuedRender) return;
if (_hasNeverRendered || ShouldRender())
{
_hasPendingQueuedRender = true;
try
{
_renderHandle.Render(_renderFragment);
}
catch
{
_hasPendingQueuedRender = false;
throw;
}
}
}
StateHasChanged
only queues a new render event if one isn't already queued. Once rendered, the Renderer diffing engine detects any changes and sends just those changes to the Client Browser Session over SignalR.
So no changes, no client activity, just lots of server bound activity dealing with the events and working out any changes. The impact on the server will depend upon how much server power you have available.
Upvotes: 3