rstudio / httpuv

HTTP and WebSocket server package for R
Other
226 stars 86 forks source link

httpuv::service does not seem to be working #148

Closed harveyl888 closed 6 years ago

harveyl888 commented 6 years ago

I'm having some issues using httpuv::service as a way to monitor progress and interrupt execution of a shiny app using a button. It's worked in the past but now does not seem to be working, possibly due to an upgrade in shiny or httpuv.

By way of example I've found that the code at https://stackoverflow.com/questions/30587883/is-it-possible-to-stop-executing-of-r-code-inside-shiny-without-stopping-the-sh/34517844#34517844 works under some installations but not others. The app should be interrupted when the stop button is pressed.

The code works on an old server running shiny 0.12.2.9006 and httpuv 1.3.3 The code does not work under shiny 1.1.0 and httpuv 1.4.3

library(shiny)
analyze <- function(session=shiny::getDefaultReactiveDomain()){
  continue = TRUE
  lapply(1:100, function(x) {
    if(continue){
      print(x)
      Sys.sleep(1)
      # reload inputs
      httpuv:::service()
      continue <<- !isTRUE(session$input$stopThis)
    }
  }
  )
}

shinyApp(
  ui = fluidPage(
    actionButton("start","Start",class="btn-primary", onclick="Shiny.onInputChange('stopThis',false)"),
    actionButton("stop","Stop",class="btn-danger", onclick="Shiny.onInputChange('stopThis',true)")
  ),
  server = function(input, output, session) {
    observeEvent(input$start, {
      analyze()
    })
  }
)
wch commented 6 years ago

That's definitely not how service() is meant to be used -- runApp() indirectly calls service(), and it in turn makes all the machinery in Shiny run. The effect of your code is to recursively call service(), which is not how it's supposed to work.

That said, this is an interesting use case, and I think we can come up with a way to do what you're looking for using Shiny's new async features. Stay tuned...

harveyl888 commented 6 years ago

Thanks Winston. Shame - it was really useful to be able to monitor a reactive variable during a long process and then pause or break as appropriate. Would be interested if there's a solution using async programming.

jcheng5 commented 6 years ago

@harveyl888 I have a number of ways to solve this that we can talk about. It'd be helpful to know from you:

  1. Are the tasks in question like the example you posted, in that, you're doing homogeneous operations on a list of data? Or was that just an example, and actually you've got a sequence of heterogeneous steps that you want to be able to interrupt in between them?
  2. Would it be desirable to execute these operations outside of the main R process? Or do you want it to stay in-process for some reason?
harveyl888 commented 6 years ago

Hi Joe. Thanks in advance.

  1. The example above covers pretty much what I'm aiming to do. It's a series of operations within a loop. I'd like to break the loop upon a button-press but not exit the app.
  2. I don't see why the operations couldn't run in outside the main R process. They currently reside in a module and depend on some additional functions. When interrupted a reactiveValue is changed. Would processes be able to share reactiveValues? Hope this helps. I can put together an MWE but the example above is quite close to the code I have, except for the use of a global variable.
jcheng5 commented 6 years ago

Here's a start:

https://gist.github.com/jcheng5/1ff1efbc539542ecedde92f25458a872

We should be able to do much better than this (for example, automatically stopping if the Shiny session that started the task stops; and working with async) but hopefully this can get you unblocked for now.

harveyl888 commented 6 years ago

Thanks @jcheng5. I've taken the gist and found that you can run with reactiveValues if func is wrapped in isolate. This is a great help and will certainly set me down the right path. Thanks again for all the help!

jcheng5 commented 6 years ago

@harveyl888 Ah, I forgot to answer the reactiveValues part of your question. When running in the same process, you can totally do that, but keep in mind those values may change under you as the user continues to use the application. If you want to "snapshot" the reactiveValues at the start time of the operation, you can do that by reading the reactiveValues you want (with isolate) into temp variables. When running in a different process, the values won't change under you, but you may end up copying more data into the background process then you actually need to; again, the solution is to copy the values you want (with isolate) into temp variables, and those will be the only ones copied to the background process.