Shiny promises future is not working on eventReactive
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.
R Shiny promise/future blocks process
Try this:
library(data.table)
library(ggplot2)
library(promises)
library(future)
plan(multiprocess)
server <- function(input, output, session) {
dt_trend <- eventReactive(
input$run_trends,
{
dat_func <- function() {
start_time <- Sys.time()
dt <- data.table(x = rnorm(100), y = rnorm(100))
trendy_tbl <- head(dt, 10)
ggplo1 <- ggplot(dt) + geom_point(aes(x=x,y=y))
Sys.sleep(10)
list(
trendy_tbl
, ggplo1
, paste0('time: ', round(Sys.time() - start_time), ' сек.')
)
}
# Returning future
future({
dat_func()
})
})
output$trend_tbl <- renderDT({dt_trend()[[1]]})
output$trend_plotly <- renderPlot({dt_trend()[[2]]})
output$trends_time <- renderText({dt_trend()[[3]]})
}
The key ideas are:
- Make sure you use
shiny
version > 1.1.0 (April 2018) which introduced async support. - do NOT use
future::value
as it blocks and waits for the future, precisely what we want to avoid. - instead, return the future in a
reactive
. In this case, this means usingeventReactive
instead ofobserveEvent
. - Access the value of the future via the reactive. Note that your reactive value is now a future! This means you need to use future handlers to use the value. (*)
- You can also return a future in any
renderXXX
function. Useful for example if you have large time-consuming plots.
(*) In practice, this means you need to do
renderDT(dt_trend() %>% then(~.[[1]]))
# or
renderDT(dt_trend() %>...% `[[`(1))
where then
is from the promises
package and [[
is the subsetting function from base R (x[[i]]
is actually semantic sugar for`[[`(x, i)
!).
In your example, you basically compute everything in a single future
in dt_trend
. You may want to consider using multiple small futures instead. You can load the data in a reactive with a future, then keep the output code in your renderXXX
functions, wrapped in a future
if needed.
There is a good vignette on using promises with shiny available by running vignette("shiny", package = "promises")
(**). It is also available online on cran or on the rstudio blog.
(**) If you installed promises with install_github("rstudio/promises")
, you most likely have to re-install with build_vignettes = TRUE
first.
How to make an operation uninterruptible in R shiny
Thanks @Shree for pointing out the solution. After reading the response from Joe Cheng. It seems like the key is to:
Hide the async operation from Shiny by not having the promise be the last expression.
The problem is resolved by creating a reactive value and assign the promise to it in observeEvent
as the side effect.
server <- function(input, output, session) {
output$time <- renderText({
invalidateLater(1000)
as.character(Sys.time())
})
process <- reactiveVal()
observeEvent(input$button,{
output$isbusy <- renderText("busy") # a simple busy indicator
future({
Sys.sleep(5)
runif(1)
}) %...>%
process()
# Hide the async operation from Shiny by not having the promise be the last expression
NULL # important
})
output$result <- renderText({
output$isbusy <- renderText("") # a simple busy indicator
process()
})
}
why does future::future() not work with dbAppendTable in shiny?
The DBI
package and all their implementations (like RSQLite
) do use non-exportable objects (basically memory pointers), see:
https://cran.r-project.org/web/packages/future/vignettes/future-4-non-exportable-objects.html
You can see an error message when you configure futures
like this:
options(future.globals.onReference = "error")
# ... your code goes here
# Error in FALSE :
# Detected a non-exportable reference (‘externalptr’) in one of the globals (‘con’ of class ‘SQLiteConnection’) used in the future expression
# Timing stopped at: 0.028 0 0.028
Related Topics
Using R to Connect to a Sharepoint List
How to Get Environment of a Variable in R
Fixing Variance Values in Lme4
Create and Call Linear Models from List
Can You Pass a Vector to a Vararg: Vector to Sprintf
Generate Random Integers Between Two Values with a Given Probability Using R
Shiny Dashboard Mainpanel Height Issue
Ggplot2 Add a Legend for Several Stat_Functions
R: Replacing Foreign Characters in a String
Ggplot2': Label Values of Barplot That Uses 'Fun.Y="Mean"' of 'Stat_Summary'
R Output Without [1], How to Nicely Format
Rcurl: Http Authentication When Site Responds with Http 401 Code Without Www-Authenticate
Importing Multiple Excel Files with Filenames in R
Draw a Trend Line Using Ggplot
Boxplot, How to Match Outliers' Color to Fill Aesthetics
Replace Nas in One Variable with Values from Another Variable