Shiny promises future is not working on eventReactive
Asked Answered
B

1

5

I have an inputButton than when you click it, 2 querys to mysql database are done. One is a heavy one (more than 10 secs) and the other is light (less than 0.01sec to get data).

As I want to show the result of this querys on shiny app, I have intendeed to use Promises and Future packages for asyncronous loading.

In the example that I show you of my code, I have simulated the SQL querys with the function heavyFunction, which is intended to simulate the heavy query and the ligth one time loads.

The issue is that this code is not working for me, because the results of the light query are not shown till the heavy query is done.

Note: In the Rstudio console, this code works perfect...

library(future)
library(promises)
plan(multiprocess)

heavyFunction <- function(n){
  Sys.sleep(n)
  print(n)
}


ui <- fluidPage(
  actionButton("go","Show the data"),
  textOutput("result0sec"),
  textOutput("result10sec")

)

server <- function(input,output,session){


  data0 <- eventReactive(input$go,{
    heavyFunction(0)


  })

  data10 <- eventReactive(input$go,{
    heavyFunction(10)


  })
  output$result0sec <- renderText({
  data <- data0()
  future(data)%...>%print()
  })


  output$result10sec <- renderText({
    data <- data10()
    print(data)
  })




}
shinyApp(ui,server)

What I'm doing wrong?

Bedtime answered 21/11, 2018 at 7:17 Comment(0)
R
7

Welcome to SO!

This thread discusses the same issue.

Please also see the detailed answer from Joe Cheng on GitHub.

The main problem you are experiencing is reflected by his following statement:

The goal, at least for this release of Shiny, is not to allow this kind of intra-session responsiveness, but rather, inter-session; i.e., running an async operation won't make its owning session more responsive, but rather will allow other sessions to be more responsive.

However, there are ways to work around this behaviour by running the future in a background R process with e.g. library(callr) or more convenient library(future.callr) and it's plan(callr).

Here is a working version of your code:

library(future)
library(promises)
library(future.callr)
plan(callr)

heavyFunction <- function(n) {
  Sys.sleep(n)
  print(n)
}

ui <- fluidPage(
  br(),
  actionButton("go", "Show the data"),
  br(), br(),
  textOutput("result0sec"),
  textOutput("result10sec")
)

server <- function(input, output, session) {
  futureData <- reactiveValues(data10 = NULL)

  data0 <- eventReactive(input$go, {
    heavyFunction(0)
  })

  observeEvent(input$go, {
    myFuture <- future({
      heavyFunction(5)
    })

    then(
      myFuture,
      onFulfilled = function(value) {
        futureData$data10 <<- value
      },
      onRejected = NULL
    )
    return(NULL)
  })

  output$result0sec <- renderText({
    data0()
  })

  output$result10sec <- renderText({
    req(futureData$data10)
  })
}

shinyApp(ui, server)

The most important point here is to realize, that you shouldn't return your future directly, otherwise it will block all other actions - the observer returns nothing, it only has the side-effect of triggering the callback-function.

Recurrence answered 21/11, 2018 at 14:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.