rstudio / promises

A promise library for R
https://rstudio.github.io/promises
Other
201 stars 19 forks source link

Async process blocks shiny app within "user session" #23

Closed raphaelvannson closed 4 months ago

raphaelvannson commented 6 years ago

Hello,

I am having trouble making a simple shiny app with a non-blocking async process. I am not a beginner in R or multi-process programming, read the documentation thoroughly yet I cannot get this to work how it should so I am posting a question here in the hopes you can help me figure out what I am doing wrong.

Environment

Mac OS 10.12

$ R --version
R version 3.4.3 (2017-11-30) -- "Kite-Eating Tree"
remove.packages("future")
remove.packages("promises")
remove.packages("shiny")

install.packages("future")
install.packages("devtools")
devtools::install_github("rstudio/promises")
devtools::install_github("rstudio/shiny")

> packageVersion("future")
[1] ‘1.8.1’
> packageVersion("promises")
[1] ‘1.0.1’
> packageVersion("shiny")
[1] ‘1.0.5.9000’

One side question on the shiny package version: https://rstudio.github.io/promises/articles/intro.html says it should be >=1.1, but even installing with devtools, the version remains 1.0.5... . Is this an issue or is there a typo in the doc?

First, you can use promises with Shiny outputs. If you’re using an async-compatible version of Shiny (version >=1.1), all of the built-in renderXXX functions can deal with either regular values or promises.

Example of issue

I have implemented this simple shiny app inspired from the example at the URL mentioned above and the vignettes mentioned below. The shiny app has 2 "sections":

  1. A button to trigger the "long running" async processing. This is simulated by a function read_csv_async which sleeps for a few seconds, reads a csv file into a data frame. The df is then rendered below the button.
  2. A simple functionality which should work at any time (including when the async processing has been triggered): it includes a slider defining a number of random values to be generated. We then render a histogram of these values.

The issue is that the second functionality (histogram plot update) is blocked while the async processing is occurring.

global.R

library("shiny")
library("promises")
library("dplyr")
library("future")

# path containing all files, including ui.R and server.R
setwd("/path/to/my/shiny/app/dir")   

plan(multiprocess)

# A function to simulate a long running process
read_csv_async = function(sleep, path){
      log_path = "./mylog.log"
      pid = Sys.getpid()
      write(x = paste(format(Sys.time(), "%Y-%m-%d %H:%M:%OS"), "pid:", pid, "Async process started"), file = log_path, append = TRUE)
      Sys.sleep(sleep)
      df = read.csv(path)
      write(x = paste(format(Sys.time(), "%Y-%m-%d %H:%M:%OS"), "pid:", pid, "Async process work completed\n"), file = log_path, append = TRUE)
      df = read.csv(path)
      df
}

ui.R

fluidPage(
  actionButton(inputId = "submit_and_retrieve", label = "Submit short async analysis"),
  br(),
  br(),
  tableOutput("user_content"),

  br(),
  br(),
  br(),
  hr(),

  sliderInput(inputId = "hist_slider_val",
              label = "Histogram slider",
              value = 25, 
              min = 1,
              max = 100),

  plotOutput("userHist")
)

server.R

function(input, output){
   parent_pid = Sys.getpid()

    # When button is clicked
    # load csv asynchronously and render table
    data_promise = eventReactive(input$submit_and_retrieve, {
        future({ read_csv_async(10, "./data.csv") }) 
    })
   output$user_content <- renderTable({
     data_promise() %...>% head(5)
    })

  # Render a new histogram 
  # every time the slider is moved
  output$userHist = renderPlot({
    hist(rnorm(input$hist_slider_val))
  })
}

data.csv

Column1,Column2
foo,2
bar,5
baz,0

Question

I can't get the non-blocking async processing to work in shiny: the histogram update is always blocked while the async process is running. I have tried other strategies involving observeEvent() or even simpler examples with the same resutls. Can you provide a simple example of a shiny app including a non-blocking example of an async processing or let me know what I am doing wrong here?

I have thoroughly read the vignettes listed below: https://cran.r-project.org/web/packages/promises/vignettes/intro.html https://cran.r-project.org/web/packages/promises/vignettes/overview.html https://cran.r-project.org/web/packages/promises/vignettes/futures.html https://cran.r-project.org/web/packages/promises/vignettes/shiny.html

Thanks!