Bennyboy1973
Bennyboy1973

Reputation: 4208

Timer sometimes not triggering

---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:

(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

Answers (3)

Bennyboy1973
Bennyboy1973

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

Henk Holterman
Henk Holterman

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

Henk Holterman
Henk Holterman

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).

  • make sure you only have 1 Timer instance on your page
  • create and start it in OnInitialized()
  • use @implements IDisposable to clean up

Here is some sample code.

Upvotes: 1

Related Questions