Simon_Weaver
Simon_Weaver

Reputation: 145880

Async friendly DispatcherTimer wrapper/subclass

I have a DispatcherTimer running in my code that fire every 30 seconds to update system status from the server. The timer fires in the client even if I'm debugging my server code so if I've been debugging for 5 minutes I may end up with a dozen timeouts in the client. Finally decided I needed to fix this so looking to make a more async / await friendly DispatcherTimer.

Upvotes: 6

Views: 7779

Answers (1)

Simon_Weaver
Simon_Weaver

Reputation: 145880

Here's what I came up with.

  • SmartDispatcherTimer Extends DispatcherTimer (was easiest way to get this up and running)
  • Has a TickTask property to provide a Task to handle the logic
  • Has an IsReentrant property (of course the whole point is that I want it to not be reentrant so normally this is false)
  • It assumes anything you're calling is fully awaitable - or you'd end up losing the reentrancy protection benefits

Usage:

        var timer = new SmartDispatcherTimer();
        timer.IsReentrant = false;
        timer.Interval = TimeSpan.FromSeconds(30);
        timer.TickTask = async () =>
        {
            StatusMessage = "Updating...";  // MVVM property
            await UpdateSystemStatus(false);
            StatusMessage = "Updated at " + DateTime.Now;
        };
        timer.Start();

Here's the code. Would love to hear any thoughts on it

public class SmartDispatcherTimer : DispatcherTimer
{
    public SmartDispatcherTimer()
    {
        base.Tick += SmartDispatcherTimer_Tick;
    }

    async void SmartDispatcherTimer_Tick(object sender, EventArgs e)
    {
        if (TickTask == null)
        {
            Debug.WriteLine("No task set!");
            return;
        }

        if (IsRunning && !IsReentrant)
        {
            // previous task hasn't completed
            Debug.WriteLine("Task already running");
            return;
        }

        try
        {
            // we're running it now
            IsRunning = true;

            Debug.WriteLine("Running Task");
            await TickTask.Invoke();
            Debug.WriteLine("Task Completed");
        }
        catch (Exception)
        {
            Debug.WriteLine("Task Failed");
        }
        finally
        {
            // allow it to run again
            IsRunning = false;
        }
    }

    public bool IsReentrant { get; set; }
    public bool IsRunning { get; private set; }

    public Func<Task> TickTask { get; set; }
}

Upvotes: 12

Related Questions