rshinyr-futurer-promises

Use future and promise inside reactive R shiny


I have a question regarding R shiny and more especially about the way to improve performance of my shiny app. I'm using some SQL queries that take quite long to run as well as some plot which take time to show up. I know that it is possible to use future and promises but I can't figure out how to use them in a reactive expressions.

I show you below a really simple example:

library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)

ui <- fluidPage(
  selectInput("id1", "Choose:", choices = unique(as.character(iris$Species))),
  dataTableOutput("my_data"),
  plotlyOutput("my_plot")
)

server <- function(input, output, session) {
  
  output$my_data <- renderDataTable({
    iris %>% filter(Species == input$id1)
  })
  
  data_to_plot <- reactive({
    Sys.sleep(10)
    res <- ggplot(iris %>% filter(Species == input$id1), aes(x=Sepal.Length)) + geom_histogram()
    return(res)
  })
  
  output$my_plot <- renderPlotly({
    ggplotly(data_to_plot())
  })
  
  
  
}

shinyApp(ui, server)

In this case, we can see that the app waits for the plot part to end computation before showing the whole app. However I'd like the datatable to show even if the part with the plot hasn't finish its computation.

Thanks a lot in advance for your help !


Solution

  • Officially shiny doesn't support intra-session non-blocking promises.

    Please read this carefully.

    Here is how to apply the above workaround to your code:

    library(shiny)
    library(ggplot2)
    library(plotly)
    library(dplyr)
    library(promises)
    library(future)
    
    plan(multisession)
    
    ui <- fluidPage(
      selectInput("id1", "Choose:", choices = unique(as.character(iris$Species))),
      dataTableOutput("my_data"),
      plotlyOutput("my_plot")
    )
    
    server <- function(input, output, session) {
      
      output$my_data <- renderDataTable({
        iris %>% filter(Species == input$id1)
      })
      
      data_to_plot <- reactiveVal()
      
      observe({
        # see: https://cran.r-project.org/web/packages/promises/vignettes/shiny.html
        # Shiny-specific caveats and limitations
        idVar <- input$id1
        
        # see https://github.com/rstudio/promises/issues/23#issuecomment-386687705
        future_promise({
          Sys.sleep(10)
          ggplot(iris %>% filter(Species == idVar), aes(x=Sepal.Length)) + geom_histogram()
          }) %...>%
          data_to_plot() %...!%  # Assign to data
          (function(e) {
            data(NULL)
            warning(e)
            session$close()
          }) # error handling
        
        # Hide the async operation from Shiny by not having the promise be
        # the last expression.
        NULL
      })
      
      output$my_plot <- renderPlotly({
        req(data_to_plot())
        ggplotly(data_to_plot())
      })
      
    }
    
    shinyApp(ui, server)