Reputation: 4208
---UPDATE---
I can now say that EVERY time the Timer fails to trigger, I get the following error message in Debug Output. And when I get the error, the Timer fails 100% of the time.
Failed to load resource: net::ERR_HTTP2_PROTOCOL_ERROR [https://localhost:44371/_framework/blazor.server.js] Exception thrown: 'Microsoft.AspNetCore.Connections.ConnectionResetException' in Microsoft.AspNetCore.Server.IIS.dll
However, the live site (on a Windows server) also fails to trigger the Timer sometimes, so I don't think it's just VS debugging issue.
---/UPDATE---
I have an animated svg on my foyer page which lasts 3 seconds. I would like to display additional controls after 3 seconds: Login button, "Take a tour" and so on, so I've added a Timer which is started in OnAfterRender (also tried OnAfterRenderAsync).
The problem is that about 1/3 of the time (randomly as rarely as 1/20 or more), the handler for the timer doesn't trigger, and the controls are not shown. I'm assuming it's some kind of threading race condition, but I don't really understand what can be done about it.
I've tried:
async
lifecycle events and handlerawait ()=>
expressions and so on in various combinations(Note-- I know I can used animated CSS to do it, but I'm limit testing the use of Timers right now)
Here's some simplified code:
<MyCoolAnimation / >
@if (ShowControls)
{
// various controls
}
@code {
bool ShowControls;
protected override void OnAfterRender(bool firstRender)
{
if (!firstRender)
{
Timer = new Timer(3000);
Timer.Elapsed += CountDownTimer;
Timer.Start();
}
}
private async void CountDownTimer(Object source, System.Timers.ElapsedEventArgs e)
{
Timer.Stop();
Timer.Dispose();
ShowControls = true;
await InvokeAsync(StateHasChanged);
}
}
Upvotes: 0
Views: 821
Reputation: 4208
(v3)
Last update:
I'm pretty over this. Thanks for helping, guys! Right now, the following suggestion from Henk works. I did a Windows update, and I thought that was the issue-- but I still can't get the Timer version to work properly. I'm cautiously optimistic that the issue is with the lifecycle, not with Timers in general.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!ShowControls)
{
await Task.Delay(3000);
ShowControls = true;
StateHasChanged();
}
}
It seems to me that instantiating a Timer object inside a Blazor lifecyle even might just be too finicky to get it to work reliably.
My only hope is that other things I use Timer for (like popping up queued messages through a service) won't ALSO break 5% of the time.
Upvotes: 0
Reputation: 273244
In this case I would avoid the entire Timer issue.
You can do this witout a Timer, Dispose pattern or InvokeAsync. Just keeping it simple:
<MyCoolAnimation / >
@if (ShowControls)
{
// various controls
}
@code {
bool ShowControls;
protected override async Task OnInitializedAsync()
{
ShowControls = false;
await Task.Delay(3_000);
ShowControls = true;
}
}
I have my doubts about calling Timer.Dispose()
inside the elapsed handler. Couldn't find a authorative statement about it though.
Upvotes: 1
Reputation: 273244
if (!firstRender)
is strange - you will have many timers running.
And that kinda provides an answer, the Timer = new Timer(3000);
and the Timer.Dispose();
parts have a race condition (on Blazor Server at least).
@implements IDisposable
to clean upHere is some sample code.
Upvotes: 1