Call a function at a specific time using C # Reactive Extensions

Is it possible to call a function at a specific time using Reactive Extensions?

For example, if I want to call the foo () method from exactly 9am and 1pm, every day I could use the Timer class to check every few seconds if it is 9am or 1pm, or even the Observable.Interval function. But is there a more efficient way to do this? Therefore, I do not check every few seconds if it is time to call foo (), but rather an observable that will call foo () on its own at the right time.

+5
source share
1 answer

Just use the Timer overload, which takes a DateTimeOffset value. You can use Defer and Repeat to create an "absolute interval".

 Observable.Defer(() => DateTime.Now.Hour < 9 ? Observable.Timer(DateTime.Today.AddHours(9)) : DateTime.Now.Hour < 13 ? Observable.Timer(DateTime.Today.AddHours(13)) : Observable.Timer(DateTime.Today.AddDays(1).AddHours(9))) .Repeat() .Subscribe(...); 

Rx automatically ensures , as far as possible, your notification will occur at the exact date and time specified even with respect to the timer and if the system clock changes before the timer expires.

An extension method is used here that further generalizes the problem.

Using:

 Observable2.Daily(TimeSpan.FromHours(9), TimeSpan.FromHours(13)).Subscribe(...); 

Definition:

 public static partial class Observable2 { public static IObservable<long> Daily(params TimeSpan[] times) { Contract.Requires(times != null); Contract.Requires(Contract.ForAll(times, time => time > TimeSpan.Zero)); Contract.Requires(Contract.ForAll(times, time => time.TotalDays < 1)); return Daily(Scheduler.Default, times); } public static IObservable<long> Daily(IScheduler scheduler, params TimeSpan[] times) { Contract.Requires(times != null); Contract.Requires(Contract.ForAll(times, time => time > TimeSpan.Zero)); Contract.Requires(Contract.ForAll(times, time => time.TotalDays < 1)); if (times.Length == 0) return Observable.Never<long>(); // Do not sort in place. var sortedTimes = times.ToList(); sortedTimes.Sort(); return Observable.Defer(() => { var now = DateTime.Now; var next = sortedTimes.FirstOrDefault(time => now.TimeOfDay < time); var date = next > TimeSpan.Zero ? now.Date.Add(next) : now.Date.AddDays(1).Add(sortedTimes[0]); Debug.WriteLine("Next @" + date + " from " + sortedTimes.Aggregate("", (s, t) => s + t + ", ")); return Observable.Timer(date, scheduler); }) .Repeat() .Scan(-1L, (n, _) => n + 1); } } 

Update:

If you want to be more “functional” in your approach, specifying input as an infinite sequence from an iterator block to Jeroen Mostert’s answer, you can use Generate as follows.

 Observable.Generate( GetScheduleTimes().GetEnumerator(), e => e.MoveNext(), e => e, e => e.Current, e => e.Current); 

Jeroen Mostert's (remote) answer provided an example implementation of GetScheduleTimes , but basically it was just an iterator block that gave an infinite sequence of DateTimeOffset values ​​in a while loop, with each loop increasing the value of day 1.

+6
source

Source: https://habr.com/ru/post/1208970/


All Articles