Can the Elapsed callback of a System.Timers.Timer be async?
Asked Answered
B

4

19

Is it possible (or even reasonable) to make the callback of a System.Timers.Timer an async method? Something like:

var timer = new System.Timers.Timer
{
   Interval = TimeSpan.FromSeconds(30).TotalMilliseconds,
   AutoReset = true
};
timer.Elapsed += async (sender, e) => { /* await something */ };
timer.Start();

It compiles (obviously a good place to start), but I'm not sure I understand the consequences. Will the timer await the callback before resetting the timer?

Brittanybritte answered 27/1, 2015 at 18:12 Comment(0)
P
31

Will the timer await the callback before resetting the timer?

No. There's nothing it could await, because the signature of ElapsedEventHandler has a void return type.

In other words, your code is equivalent to:

var timer = new System.Timers.Timer { ... };
timer.Elapsed += Foo;
timer.Start();

...
private async void Foo()
{
    ...
}

Whether that's acceptable for you or not will depend on your context. In general, having async void methods or anonymous functions makes them harder to test and reuse - but the ability was precisely given for the sake of event handlers... You should consider how errors will be propagated though.

Pincushion answered 27/1, 2015 at 18:16 Comment(0)
U
15

The title of the question is specifically about Timers, but if we look at it as "How to call an async method after some time?" then you could do it without using a timer.

var task2 = Task.Run(async () => {
    while (true)
    {
        try
        {
            await MyMethod2();
        } catch
        {
            //super easy error handling
        }
        await Task.Delay(TimeSpan.FromSeconds(5));
    }
});

...

public async Task MyMethod2()
{
    //async work here
}

Please note however that this will have different timing (timer will be called at an interval, the code above will be called every (run time + sleep_time), but even if MyMethod2 takes a long time it it won't be called twice. Having said that, you can calculate how long to await for to run 'every x minutes'.

Unpeg answered 28/7, 2017 at 3:53 Comment(2)
Used this for slowing down webrequests on Azure. More than 10 per second caused an error. Thanks.Adalbertoadalheid
It sounds good. I will try to test it in a service. I usually use a timmer but this time OnElapsed I have to make several secuential async calls. It may works! Thank you very much!Weatherspoon
W
-1

The solution that proposed @tymtam doesn´t wait until MyMethod2 has ended.
I think it would be better to use this.
An example with two async tasks, when both have finished, wait 5 seconds and execute again the two tasks:

var task2 = Task.Run(async () => {
    while (true)
    {
        try
        {
            var task1 = MyMethod1();
            var task2 = MyMethod2();
            List<Task> allTasks = new List<Task> { task1, task2 };
            while (allTasks.Count > 0)
            {
                Task finishedTask = await Task.WhenAny(allTasks);
                if (finishedTask == task1)
                {
                   Console.WriteLine("MyMethod1 has ended");
                }
                else if (finishedTask == task2)
                {
                   Console.WriteLine("MyMethod2 has ended");
                }
                tareas.Remove(finishedTask);
            }
            //Here only when finished all task
        } catch
        {
            //super easy error handling
        }
        //Wait until next cycle
        await Task.Delay(TimeSpan.FromSeconds(5));
    }
});

...

public async Task MyMethod1()
{
    //async work here
}

public async Task MyMethod2()
{
    //async work here
}
Weatherspoon answered 1/8, 2021 at 13:31 Comment(1)
tareas.Remove(finishedTask); the tareas variable is not defined.Borroff
C
-2

Actually, you can.

System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += async (x, y) => { await Task.Delay(1); };
Colly answered 17/1, 2021 at 19:17 Comment(1)
For what it's worth, trying this caused my entire program to crash.Brickyard

© 2022 - 2024 — McMap. All rights reserved.