Understanding session$onFlush in shiny

Viewed 424

I have problems understanding the onFlush function in shiny on when registered function get exactly called.

Looking at its reference: https://www.rdocumentation.org/packages/shiny/versions/1.6.0/topics/onFlush it suggest that registered functions will be triggered before the observers and render functions get recalculated.

Since it states the following:

onFlush registers a function that will be called before Shiny flushes the reactive system.

What I understand as a flush is the re-computation of all the observers and render functions. This idea is supported by one of their official articles, stating:

Once all the descendants are invalidated, a flush occurs. When this happens, all invalidated observers re-execute.


Thus when I run the following minimal example:

library(shiny)

ui <- fluidPage( actionButton("invalidate", "invalidate")  )
    
server <- function(input,output,session){
  
  session$onFlush( function() message("Start flush"), once = FALSE)
  session$onFlushed( function() message("End flush"), once = FALSE )
  
  observe({
     Sys.sleep(3) # Long computation  
     input$invalidate
    })
}

shinyApp(ui, server)

I expect to see first the message: Start flush then a 3 second pause and then the message End flush.

In reality however, the start flush is only executed after the observe was executed, while I would have thought that this would get executed before the observe.

At first, I thought this was a bug, however I then found this official shiny example: https://github.com/rstudio/shiny-examples/blob/master/127-async-flush/app.R and learned that this order of execution is actually the intended behavior.

So now I am actually not sure how I need to interpret the onFlush function or what the flush is in the first place.

1 Answers

I think it is best to interpret the onFlush function in a similar way on.exit is working in a non-reactive context. We can read both as "before flush" and "before exit", since they both execute functions before a flush occurs or before a functions exits. This is the reason why the start flush message is shown after the observe was executed.

I changed the app a little to make the example easier to follow (at least I hope it helps):

library(shiny)

ui <- fluidPage( actionButton("invalidate", "invalidate")  )

server <- function(input,output,session){
  
  session$onFlush( function() message("Start flush"), once = FALSE)
  session$onFlushed( function() message("End flush"), once = FALSE )
  
  observeEvent(input$invalidate, {
    print(input$invalidate)
    Sys.sleep(3) # Long computation    
  }, 
  ignoreInit = TRUE)
}

shinyApp(ui, server)

I think the following is happening when we click on the actionButton;

  1. the value of input$invalidate changes from 0 to 1
  2. change of input$invalidate triggers observeEvent
  3. the observeEvent is executed (print and Sys.sleep)
  4. the observeEvent is invalidated
  5. onFlush prints "Start flush"
  6. a flush occurs
  7. onFlushed print "End flush"
  8. The observeEvent is re-executed and now listens to the new value of input$invalidate

There is almost no time difference between before and after the flush. That is the reason both messages are printed directly after each other.

Related