简体   繁体   中英

Using reactivity (in Shiny) from a function

I am writing a function for my app that will call a process in the background, and monitor the moment it stops. Upon closing the session, the process should be killed, so I want it to be available outside of the function. If I assign the process with the <<- sign, something strange happens...

library(shiny)
library(processx)


runAsync <- function() {

  mainProcess <<- process$new("sleep", "10")

  procTimer <- reactivePoll(1000, NULL, 
                            checkFunc = function() 
                              mainProcess$is_alive() ,
                            valueFunc = function() 
                                mainProcess$is_alive()
                            )
  
  observeEvent(procTimer(),  {
    if(procTimer()) {print("Begin")} else {print("End")}
    print(  mainProcess)
  })
}



ui <- fluidPage(
  actionButton("runBtn", "Run async process"),
)

server <- function(input, output, session) {
  observeEvent(input$runBtn, runAsync())  
}

shinyApp(ui, server)

The first time that the button is clicked, everything works as expected. The output is

[1] "Begin"
PROCESS 'sleep', running, pid #####.
[1] "End"
PROCESS 'sleep', finished.

On the second time however, observeEvent is triggered twice. On the third time, observedEvent is triggered thrice. And so on... The output when clicking on the button for the third time is

PROCESS 'sleep', running, pid 19228.
[1] "Begin"
PROCESS 'sleep', running, pid 19228.
[1] "Begin"
PROCESS 'sleep', running, pid 19228.
[1] "End"
PROCESS 'sleep', finished.
[1] "End"
PROCESS 'sleep', finished.
[1] "End"
PROCESS 'sleep', finished.

If I replace the <<- assignment with a simple <- assignment, that fixes it (at least on the surface), and the problem does not occur.

So, my main question is what is the cause of this behaviour? I'd like to understand .

I'd also like to ask a side question: what is a good design for such a function? Is it good practice to start a reactivePoll within a function?

The problem is that everytime you call runAsync() you add a new observeEvent call, a quick fix would be to introduce a reactive value that checks if the function was already called and then do not add a new observeEvent as shown in that example:

library(shiny)
library(processx)


runAsync <- function(start) {
  
  mainProcess <<- process$new("sleep", "10")
  
  procTimer <- reactivePoll(1000, NULL, 
                            checkFunc = function() 
                              mainProcess$is_alive() ,
                            valueFunc = function() 
                              mainProcess$is_alive()
  )
  if(isTRUE(reactive_value$start))
  {
    observeEvent(procTimer(),  {
      if(procTimer()) {print("Begin")} else {print("End")}
      print(  mainProcess)
    })
    reactive_value$start <- FALSE
  }

}

ui <- fluidPage(
  actionButton("runBtn", "Run async process"),
)
reactive_value <- shiny::reactiveValues(start = TRUE)

server <- function(input, output, session) {
  observeEvent(input$runBtn, runAsync(reactive_value))  
}

shinyApp(ui, server)

The longer solution would be to restrucutre the code in a way that the observeEvent call is outside of the runAsync function

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM