-1

I am trying to write a loop that allows running R code at a prespecified time.

This is typically done using task manager/taskscheduleR in windows. But for this particular task, I need to perform it purely on r as i will transfer it into a small shiny app. I sent this question here for the shiny part of it (Run shiny events at specified system times ), for now I need to make sure I have the appropriate code in R, then will work on shiny as next step.

this 2012 post ( I want to run a R code at a specific time ), mentioned a method I tried below but it did not work. It is supposed to print "It's time!" and opens the R project webpage 10 seconds after code is run.

time_to_run =  Sys.time()+10 #this defines the "target time", in this case, is 10 seconds later
while(TRUE) {
 
  if(Sys.time() == time_to_run) {
    print("It's time!")
    browseURL("https://www.r-project.org")
  }}

I also tried using repeat loop, but again without adequate response:

time_to_run =  Sys.time()+10
repeat {
  if(Sys.time() == time_to_run) {
    print("it's time!")
    browseURL("https://www.r-project.org")
 
  }
 
}

Any suggestions why this is not working?

thanks in advance.

Update:

Thanks to the comment below from Jon Spring, I leant how to compare Sys.time() after rounding to seconds.

what I do not understand now is: if I do not add break to the repeat loop, the code keeps opening the webpage endless number of times (if you try it, the computer may freeze, or you make have to restart r). isn't it supposed to run the code (ie open the webpage) only once when the condition is met and the two time points are equal? is there a way to make sure this happens?

thank you

library(lubridate)

time_to_run =  Sys.time()+5

 
repeat {
  if(round_date(Sys.time(),  unit = "second") == round_date(time_to_run,  unit = "second")) {
    print("it's time!")
    browseURL("https://www.r-project.org")
    break
  }
 
}

This is the final code I used and it is working, posting in case someone needs it. there are probably better ways to do the same task (eg shiny) but for this one I needed use in r

library(lubridate)

time_to_run =  Sys.time()+61

time_to_run = as.character(format(time_to_run, "%H:%M"))
 
repeat {
  noww = as.character(format(Sys.time(), "%H:%M"))
  
  print(noww)
  
  Sys.sleep(1)
  
  if(time_to_run == noww) {
    print("it's time!")
    browseURL("https://www.r-project.org")
    break
  }
 
}
Bahi8482
  • 489
  • 5
  • 15
  • 2
    Sys.time() uses fractional seconds. Your loop might take a few microseconds or milliseconds to run, so it will tend to not be testing for equality at the instant you reach `time_to_run`. Also, floating point numbers are notoriously difficult to compare for equality, in any computer language: https://www.geeksforgeeks.org/problem-in-comparing-floating-point-numbers-and-how-to-compare-them-correctly/. I suggest you round Sys.time() and your `time_to_run`, or define a window of time between `time_to_run` and `time_to_run + [tiny bit of time]` – Jon Spring Apr 28 '21 at 21:09
  • To see an example, try running `for(i in 1:10) { print(as.numeric(Sys.time()) * 1E6) }` On my computer, there are gaps of 30-100 ticks between each outputted number. If your comparison time is one of those ticks, it'll never match up. – Jon Spring Apr 28 '21 at 21:18
  • @JonSpring this makes complete sense. Appreciate pointing out this and explaining it. I tried this code and it works. now need to make it work in the shiny app ! : library(lubridate) if(round_date(Sys.time(), unit = "second") == round_date(time_to_run, unit = "second")) – Bahi8482 Apr 28 '21 at 21:39
  • 1
    You do realise that the answer you linked to specifically says NOT to do this – Hong Ooi Apr 28 '21 at 21:59
  • @HongOoi, yes I do and I agree this is not ideal. I have been searching this for a week now and ran out of options. I am trying to get this to work on shiny https://stackoverflow.com/questions/67237354/run-shiny-events-at-specified-system-times, if you have any suggestions for that, will be extremely helpful. thanks a lot for your help and your answer below. – Bahi8482 Apr 28 '21 at 22:04
  • 1
    One approach: create a variable `monitor = TRUE`. Then set your trigger as `if(Sys.time() > time_to_run & monitor) { monitor = FALSE; [run your code here]; if(Sys.time() > time_to_run + 1000) monitor = TRUE}`. Then the code will run once you've passed the trigger moment, then disengage checking until a bit (+1000s) later. – Jon Spring Apr 28 '21 at 22:17
  • Go to the answer you linked to and scroll down – Hong Ooi Apr 28 '21 at 23:08
  • @JonSpring thank you. this is the best answer I got. – Bahi8482 Apr 28 '21 at 23:32

1 Answers1

1

Don't do busy-loops. Use Sys.sleep:

Sys.sleep(10)
print("it's time!")
browseURL("https://www.r-project.org")

If you want to run at a set time, rather than a set delay:

at_time <- Sys.time() + 3600  # 1 hour from now, or whenever

Sys.sleep(as.numeric(at_time - Sys.time())/1000)
print("it's time!")
browseURL("https://www.r-project.org")
Hong Ooi
  • 56,353
  • 13
  • 134
  • 187