-->

Shiny: How to stop processing invalidateLater() af

2019-02-21 04:44发布

问题:

I want to keep reflashing until 10:05, after 10:05 I got the tplus0_dt and stop to processing invalidaterLater().

Before 10:00, tplus0_dt doesn't exist, so I need to keep reflashing until 10:05. After 10:05, it is no need to refalsh, and when the tplus0_dt becomes very lage the invalidaterLater() will effects the displaying of table1, the screen and plots go GRAY every 1 seconds so it looks like the page is dead while the updating occurs.

So how can I do to stop processing the invalidateLater() and keep showing the data after 10:05? Thanks for help! My example code were below.

require(shiny)
require(data.table)
app <- shinyApp(
    server = function(input, output, session){
            get_tplus0_data <- reactive({
                    time <- substr(as.character(Sys.time()), 12, 16)
                    invalidateLater(1000)
                    if(time >= "10:05"){
                            # tplus0_dt<-data.table(read.csv("/data/df_highest_after_signal.csv",header = T, sep = ",", stringsAsFactors = F)) 
                            tplus0_dt<- data.table(a = c(1, 2, 3, 4), b = c(3, 4, 5, 8)) 
                            return(tplus0_dt)
                    }

            })
            output$table1 <- renderTable({get_tplus0_data()})
    },
    ui = fluidPage( tableOutput("table1")  )
)

runApp(app)

回答1:

How about you override the function to your needs?

If you enter invalidateLaterNew in the console, the code of the function will be printed.

To overwrite a function within a package this post will help: Override a function that is imported in a namespace

Then you will have to consider that the functions .getReactiveEnvironment() and timerCallbacks() are not accessible outside the namespace. But you can call them like this: shiny:::.getReactiveEnvironment()

Bring it together:

You add an additional parameter (e.g. update), which will enable you to stop the invalideLater() whenever you want.

invalidateLaterNew <- function (millis, session = getDefaultReactiveDomain(), update = TRUE) 
{
  if(update){
    ctx <- shiny:::.getReactiveEnvironment()$currentContext()
    shiny:::timerCallbacks$schedule(millis, function() {
      if (!is.null(session) && session$isClosed()) {
        return(invisible())
      }
      ctx$invalidate()
    })
    invisible()
  }
}

unlockBinding("invalidateLater", as.environment("package:shiny"))
assign("invalidateLater", invalidateLaterNew, "package:shiny")

Example:

I used the example given in ?invalidateLater to demonstrate the effect: (invalidateLater will stop when input$nis bigger than 800. So you can adapt this example to your time restriction). I decided not to use your time restriction example as it wouldnt be that handy to test ;)

ui <- fluidPage(
  sliderInput("n", "Number of observations", 2, 1000, 500),
  plotOutput("plot")
)

server <- function(input, output, session) {

  observe({
    # Re-execute this reactive expression after 1000 milliseconds
    invalidateLater(1000, session, input$n < 800)
    # Do something each time this is invalidated.
    # The isolate() makes this observer _not_ get invalidated and re-executed
    # when input$n changes.
    print(paste("The value of input$n is", isolate(input$n)))
  })

  # Generate a new histogram at timed intervals, but not when
  # input$n changes.
  output$plot <- renderPlot({
    # Re-execute this reactive expression after 2000 milliseconds
    invalidateLater(2000, session, input$n < 800)
    hist(rnorm(isolate(input$n)))
  })
}

shinyApp(ui, server)


回答2:

Although you'd never realize it from the Shiny documentation, invalidateLater() actually only returns to your reactive once. The reason it seems to return repeatedly is that on each trip the invalidateLater() function gets run again.

So the solution is to use a conditional around the function so that you don't keep repeatedly calling it:

if(runMeAgain) {
   invalidateLater(n)
}

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

Also note that:

  • invalidateLater() is non-blocking (other code can run while you wait)
  • invalidateLater() 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()
  • invalidateLater() gets isolated() inside an observeEvent() or eventReactive() and consequently doesn't work; you have to use observe() or reactive(). It might also work inside a render function, but I haven't ever had a reason to try that.