Run async method regularly with specified interval Run async method regularly with specified interval asp.net asp.net

Run async method regularly with specified interval


The async equivalent is a while loop with Task.Delay (which internally uses a System.Threading.Timer):

public async Task PeriodicFooAsync(TimeSpan interval, CancellationToken cancellationToken){    while (true)    {        await FooAsync();        await Task.Delay(interval, cancellationToken)    }}

It's important to pass a CancellationToken so you can stop that operation when you want (e.g. when you shut down your application).

Now, while this is relevant for .Net in general, in ASP.Net it's dangerous to do any kind of fire and forget. There are several solution for this (like HangFire), some are documented in Fire and Forget on ASP.NET by Stephen Cleary others in How to run Background Tasks in ASP.NET by Scott Hanselman


The simple way of doing this is using Tasks and a simple loop:

public async Task StartTimer(CancellationToken cancellationToken){   await Task.Run(async () =>   {      while (true)      {          DoSomething();          await Task.Delay(10000, cancellationToken);          if (cancellationToken.IsCancellationRequested)              break;      }   });}

When you want to stop the thread just abort the token:

cancellationToken.Cancel();


Here is a method that invokes an asynchronous method in periodic fashion:

public static async Task PeriodicAsync(Func<Task> action, TimeSpan interval,    CancellationToken cancellationToken = default){    while (true)    {        var delayTask = Task.Delay(interval, cancellationToken);        await action();        await delayTask;    }}

The supplied action is invoked every interval, and then the created Task is awaited. The duration of the awaiting does not affect the interval, unless it happens to be longer than that. In that case the principle of no-overlapping-execution takes precedence, and so the period will be extended to match the duration of the awaiting.

In case of exception the PeriodicAsync task will complete with failure, so if you want it to be error-resilient you should include rigorous error handling inside the action.

Usage example:

Task statisticsUploader = PeriodicAsync(async () =>{    try    {        await UploadStatisticsAsync();    }    catch (Exception ex)    {        // Log the exception    }}, TimeSpan.FromMinutes(5));