In my shiny app I have a output which should update itself continuously. But whenever I execute a long-running calculation, the output is just paused. My question is: how to make the output runs continuously and uninterruptible?
Please see the short demo below:
The clock refreshes every one second, but if I click the button which runs for 5 seconds, the clock is paused.
library(shiny)
ui <- fluidPage(
actionButton("button","Expensive calcualtion(takes 5 seconds)"),
tags$p("Current Time:"),
textOutput("time"),
tags$p("Result from clicking button:"),
textOutput("result")
)
server <- function(input, output, session) {
output$time <- renderText({
invalidateLater(1000)
as.character(Sys.time())
})
observeEvent(input$button,{
Sys.sleep(5)
output$result <- renderText(runif(1))
})
}
shinyApp(ui, server)
I tried to use future and promises to make the long-running process runs asynchronously, but it doesn't work. Where is wrong? And is there a better way for achieving this purpose?
library(shiny)
library(future)
library(promises)
plan("multisession")
ui <- fluidPage(
actionButton("button","Expensive calcualtion(takes 5 seconds)"),
tags$p("Current Time:"),
textOutput("time"),
tags$p("Result from clicking button:"),
textOutput("result")
)
server <- function(input, output, session) {
output$time <- renderText({
invalidateLater(1000)
as.character(Sys.time())
})
process <- eventReactive(input$button,{
future({
Sys.sleep(5)
runif(1)
})
})
output$result <- renderText(process())
}
shinyApp(ui, server)
Any help is appreciated!
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()
})
}
Your problem is that Sys.sleep() suspends the execution of R expression. You can use things like the delay() function from shinyjs
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(), #You have to add this.
actionButton("button","Expensive calcualtion(takes 5 seconds)"),
tags$p("Current Time:"),
textOutput("time"),
tags$p("Result from clicking button:"),
textOutput("result")
)
server <- function(input, output, session) {
timer <- reactiveTimer(500)
current_time <- reactive({
timer()
as.character(Sys.time())
})
output$time <- renderText(current_time())
observeEvent(input$button,{
delay(5000, output$result <- renderText("result"))
})
}
shinyApp(ui, server)
Related
I am currently trying in vain to use the debounce function in Shiny to delay my input a bit. The goal is to have the renderText not fire every few milliseconds, but only after 2 second intervals.
I tried to implement the following solution. Thereby I absolutely need the reactiveValues and observeEvent functions. Other solutions here never take this combination into account and I am currently stuck. My example code is shortened. In reality the variable name1$data is still used by different functions and the RenderText accesses different variables.
if (interactive()) {
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactiveValues()
observeEvent(input$IText1, {Name1$data <- input$IText1})
#Solutions on stackoverflow
#Just causes errors for me
#Name1$t <- debounce(Name1$data, 2000)
output$OName <- renderText({
Name1$data
})
}
shinyApp(ui, server)
}
Thank you very much for any hint!
Normally we debounce reactive conductors (reactive({......})):
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactive({
input$IText1
})
Name1_d <- debounce(Name1, 2000)
output$OName <- renderText({
Name1_d()
})
}
shinyApp(ui, server)
EDIT
Or you need
server <- function(input, output, session) {
Name1 <- reactiveValues()
observe({
invalidateLater(2000, session)
Name1$data <- isolate(input$IText1)
})
output$OName <- renderText({
Name1$data
})
}
In following shiny application:
When click the button, it prints to console once per second.
library(shiny)
library(rlang)
ui <- fluidPage(
textOutput("text"),
actionButton("button","Click to Start")
)
server <- function(input, output, session) {
myVal <- reactiveVal(0)
startCount <- reactiveVal(FALSE)
observeEvent(input$button,{
startCount(TRUE)
})
observe({
req(startCount())
req(myVal() < 5)
invalidateLater(1000)
newVal <-myVal() + 1
# myVal(newVal)
print(myVal())
})
output$text <- renderText(myVal())
}
shinyApp(ui, server)
By removing comment # myVal(newVal), it prints 1 to 5 in the console, and displays 5 in the UI. But there are two issues:
invalidateLater stops working, it doesn't wait for one second any more.
The UI jumps to 5 instantly, instead of showing 1,2,3,4,5 with one second interval.
What should I do to make it work as intended?
You'll need isolate to avoid recursively triggering the observer:
library(shiny)
library(rlang)
ui <- fluidPage(
textOutput("text"),
actionButton("button","Click to Start")
)
server <- function(input, output, session) {
myVal <- reactiveVal(0)
startCount <- reactiveVal(FALSE)
observeEvent(input$button,{
startCount(TRUE)
})
observe({
req(startCount())
req(isolate(myVal()) < 5)
invalidateLater(1000)
isolate(myVal(myVal() + 1))
print(myVal())
})
output$text <- renderText(myVal())
}
shinyApp(ui, server)
I'm trying to get an app which updates it output after a set amount of time (i.e. to make text fade away).
In the example code, I would want "Waiting" to display on the actionButton press, then delay for 5 seconds, then the text changes to "Finished".
For some reason the whole observeEvent executes at once, so the outcome is that there is the actionButton is pressed, then there is a 5 second delay with nothing displayed, then "Finished" displays.
Sorry I don't know how to better explain the issue really - but hoping someone can help. If possible I'd like to stick to just R here, without delving too much into javascript.
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
textOutput("text"),
actionButton("press", label = "press")
)
server <- function(input, output) {
num <- reactiveVal()
observeEvent(input$press, {
output$text <- renderText("waiting")
num(1)
})
observe({
if(!is.null(num())){
output$text <- renderText({
Sys.sleep(5)
"finished"
})
}
})
}
shinyApp(ui = ui, server = server)```
You can use the delay() function from shinyjs
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
textOutput("text"),
actionButton("press", label = "press")
)
server <- function(input, output) {
num <- reactiveVal()
observeEvent(input$press, {
output$text <- renderText("waiting")
delay(5000,
output$text <- renderText("finished"))
})
}
shinyApp(ui = ui, server = server)
When I want to render multiple outputs linked to a single observer, they are rendered after both outputs are calculated. If there is a long calculation between the outputs it takes a long time before all outputs are displayed.
Is it possible in a Shiny application to render outputs, linked to a single observer, individually or in parallel? Instead of waiting with rendering until all outputs are calculated.
Example
library(shiny)
ui <- fluidPage(
actionButton('button', 'klik'),
textOutput('first'),
textOutput('second')
)
server <- function(input, output, session) {
observeEvent({input$button},{
output$first <- renderText({Sys.Date()})
Sys.sleep(10)
output$second <- renderText({Sys.Date()})
})
}
shinyApp(ui, server)
Thanks to #BertilBaron I found out a way to avoid waiting for long calculations in R-Shiny. The article that I used can be found here.
Basically what you do is to run each process in parallel using the package future. With this package every time the user clicks on the button the calculation will be performed. So keep in mind that you have to build in a block for impatient users.
My working example
library(shiny)
library(promises)
library(future)
plan(multiprocess)
ui <- fluidPage(
actionButton('button', 'klik'),
textOutput('first'),
textOutput('second')
)
server <- function(input, output) {
nclicks <- reactiveVal(0)
nclicks2 <- reactiveVal(0)
result_val <- reactiveVal()
result_val2 <- reactiveVal()
observeEvent(input$button,{
# Don't do anything if analysis is already being run
if(nclicks() != 0 | nclicks2() != 0){
showNotification("Already running analysis")
return(NULL)
}
# Increment clicks and prevent concurrent analyses
nclicks(nclicks() + 1)
nclicks2(nclicks2() + 1)
result <- future({
# Long Running Task
Sys.sleep(10)
#Some results
Sys.time()
}) %...>% result_val()
result2 <- future({
#Some results
Sys.time()
}) %...>% result_val2()
# Catch inturrupt (or any other error) and notify user
result <- catch(result,
function(e){
result_val(NULL)
print(e$message)
showNotification(e$message)
})
result2 <- catch(result2,
function(e){
result_val2(NULL)
print(e$message)
showNotification(e$message)
})
# After the promise has been evaluated set nclicks to 0 to allow for anlother Run
result <- finally(result,
function(){
nclicks(0)
})
result2 <- finally(result2,
function(){
nclicks2(0)
})
# Return something other than the promise so shiny remains responsive
NULL
})
output$first <- renderText({
req(result_val())
})
output$second <- renderText({
req(result_val2())
})
}
# Run the application
shinyApp(ui = ui, server = server)
In the following Shiny app, a long calculation is performed when an action button (run) is pressed.The output is shown at the end of the calculation.
A clock is shown in the app. While the long calculation is ongoing, the clock does not update.
Is it possible to have the clock updating while the calculation is ongoing?
Code:
library(shiny)
n <- c(1:42E7)
ui <- fluidPage(
actionButton(inputId = "run", label = "Run a long calculation"),
verbatimTextOutput("calc"),
verbatimTextOutput("time")
)
server <- function(input, output) {
x <- observeEvent(input$run,{
output$calc <- renderText({
head(sqrt(n))
})
})
output$time <- renderText({
invalidateLater(1000)
print(as.character(Sys.time()))
})
}
shinyApp(ui = ui, server = server)
Thanks in anticipation,
Chris
This answer doesn't work yet, I put it here just so you can pick up after me.
You need to update to the dev version of Shiny (remotes::install_github("rstudio/shiny")).
I replaced your toy example with a Sys.sleep() because transfering the big vector required to raise option future.globals.maxSize and was still causing my machine to crash anyway.
library(shiny)
library(promises)
library(future)
plan(multiprocess)
ui <- fluidPage(
actionButton(inputId = "run",
label = "Run a long calculation"),
verbatimTextOutput("calc"),
verbatimTextOutput("time")
)
server <- function(input, output, session) {
observeEvent(input$run,{
output$calc <- renderText({
future({
Sys.sleep(5)
42
}) %...>%
head() %...>%
print()
})
})
output$time <- renderPrint({
invalidateLater(1000)
as.character(Sys.time())
})
}
shinyApp(ui = ui, server = server)
I expected it to work, but the "expensive operation" is still blocking, I haven't figured out why yet. Hopefully it'll still be useful.
ref: https://rstudio.github.io/promises