How Shiny’s invalidateLater() actually works

Shiny’s invalidateLater() function, if you know about it at all, probably doesn’t work the way you think it does. Moreover, the way it actually does work, it turns out, is way cooler than the way it appears to work!

The main idea of invalidateLater() is to give you a buzzer that’s based on time rather than a user interface event. From inside a reactive() or observer() you give it a time in milliseconds and it re-buzzes that reactive or observer after that amount of time. Most importantly, during the interval, other Shiny code can run; invalidateLater() is non-blocking.

The Shiny documentation makes it seem like once you include invalidateLater() in a reactive, the reactive will be re-called at the time interval you specify until the session ends. Actually, however, invalidateLater() only returns to your reactive once. The reason it seems to return repeatedly is that on each trip the invalidateLater() function gets run again.

Once you realize that each call to invalidateLater() results in only one buzz-back, it becomes clear that you can use a conditional around the function to avoid repeatedly calling it:

if(runMeAgain) {
   invalidateLater(n)
}

runMeAgain = TRUE   # your reactive re-runs every n milliseconds
runMeAgain = FALSE  # your reactive does not re-run

A couple of other things. The invalidateLater() function doesn’t stop the rest of the reactive from running. If you want to stop the reactive at that point in the code, put a return() after invalidateLater().

Inside an observeEvent() or eventReactive()invalidateLater() gets isolated() and consequently doesn’t work; you have to use an observe() or reactive(). It also will work inside a render function, in fact, it’s used that way in the function’s example code.

This post is from my answer to a question about invalidateLater() on StackOverflow.com.

Leave a Reply

Your email address will not be published. Required fields are marked *