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)
Related
I've got a nested call to future_lapply that processes in parallel. It works great and fast!
The problem is that reporting on progress in sequence within Shiny is proving difficult. The following Shiny app has a progress indicator that instead of incrementing like 1,2,3, etc. increments out of order like 1, 5, 2, etc.
Any ideas on how to report progress in order (even if the processing is down out of order)? A simple counter increment doesn't work (increment <- 0; increment <- increment + 1) because of scoping rules.
Switching to plan(multisession) increments in order as expected, but that removes the parallel processing.
library(shiny)
library(future)
library(future.apply)
library(ipc)
library(callr)
plan(list(callr, tweak(multisession, workers = availableCores() -1)))
ui <- fluidPage(
actionButton("run","Run"),
tableOutput("dataset")
)
server <- function(input, output, session) {
dat <- reactiveVal()
observeEvent(input$run, {
progress <- AsyncProgress$new(session, min=1, max=15)
future({
future.apply::future_lapply(1:15, function(i){
progress$set(value = i, message = paste("Iteration:",i))
Sys.sleep(0.5)
})
progress$close()
cars
}) %...>% dat
NULL
})
output$dataset <- renderTable({
req(dat())
})
}
shinyApp(ui, server)
}
I'm new to Shiny and have hit a problem I can't find an answer for. Basically, I have a Shiny app that does some long calculations in a loop and I want it to output a "progress report" every few iterations. However, even though I reassign my reactive variable within the loop, the output doesn't update until the loop (or entire function?) has finished.
Here is a simplified test case of what I mean:
library(shiny)
# Basic interface
ui <- fluidPage(
actionButton("run", "Run"),
textOutput("textbox")
)
# Basic server with loop
server <- function(input, output) {
textvals=reactiveValues(a=0)
observeEvent(input$run, {
for(i in 1:10){
textvals$a=i # Expect output to update here, but doesn't
Sys.sleep(0.1) # Slight pause so isn't instantaneous
}
})
output$textbox <- renderText({
textvals$a
})
}
# Run the application
shinyApp(ui = ui, server = server)
What I would expect is that the display would update 1, 2, 3, ... 10 as the loop executes. Instead, it just jumps straight from 0 to 10. How can I force an update partway through the loop?
Thank you.
With using invalidateLater you can get something closed to what you want. Not the shortest way to do it I think, but it may help you to find a better solution.
library(shiny)
# Basic interface
ui <- fluidPage(
actionButton("run", "Run"),
textOutput("textbox")
)
# Basic server with loop
server <- function(input, output, session) {
textvals <- reactiveVal(0)
active <- reactiveVal(FALSE)
output$textbox <- renderText({
textvals()
})
observe({
invalidateLater(1000, session)
isolate({
if (active()) {
textvals(textvals() + 1)
if (textvals() > 9) {
active(FALSE)
}
}
})
})
observeEvent(input$run, {
active(TRUE)
})
}
# Run the application
shinyApp(ui = ui, server = server)
By the way, reactive and for loops don't really get on well. This may help : https://gist.github.com/bborgesr/e1ce7305f914f9ca762c69509dda632e
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)
I have an R Shiny dashboard that has 2 observers that are set to refresh at specific times, One observer refreshes every 6 hours, the other every 2 mins. Both observers run a function that returns a reactive value. This works fine, however every 6 hours when the first observer is triggered it locks the dashboard and prevents the other observer from functioning. After some reading I know that I need to use futures and promises but am unable to implement anything that works as intended. How do I wrap the functions in each observer into respective futures that would prevent blocking?
values <- reactiveValues()
observe({
# Re-execute this reactive expression every 2 mins
invalidateLater(120000, session)
values$twominresult <- twoMinFunction()
})
observe({
# Re-execute this reactive expression every 6 hours
invalidateLater(21600000, session)
values$sixhourresult <- sixhourfunction()
})
Here is an example for intra-session non-blocking futures based on your code snippets:
library(shiny)
library(promises)
library(future)
plan(multiprocess)
twoMinFunction <- function(){
return(Sys.time())
}
sixHourFunction <- function(){
Sys.sleep(3)
return(Sys.time())
}
server <- function(input, output, session) {
values <- reactiveValues(twominresult = NULL, sixhourresult = NULL)
observe({
# Re-execute this reactive expression every 2 seconds # mins
invalidateLater(2000, session) # 120000
myTwoMinFuture <- future({
twoMinFunction()
})
then(myTwoMinFuture, onFulfilled = function(value) {
values$twominresult <- value
},
onRejected = NULL)
return(NULL)
})
observe({
# Re-execute this reactive expression every 6 seconds # hours
invalidateLater(6000, session) # 21600000
mySixHourFuture <- future({
sixHourFunction()
})
then(mySixHourFuture, onFulfilled = function(value) {
values$sixhourresult <- value
},
onRejected = NULL)
return(NULL)
})
output$twominout <- renderText({
paste("two min result:", values$twominresult)
})
output$sixhoursout <- renderText({
paste("six hour result:", values$sixhourresult)
})
}
ui <- fluidPage(textOutput("twominout"),
textOutput("sixhoursout"))
shinyApp(ui, server)
I made it a little faster, so you can see the changes.
Please note the return(NULL) in the observeEvent() - this is hiding the future from its own session - allowing intra-session responsiveness. Please keep in mind that this pattern may cause race conditions if used the wrong way (Please see Joe Cheng's comment, which I already mentioned above)
I need some help with my Shiny app. I'll try to simplify my question. My problem is that a I'm developing an application that makes a report when a button is pressed. This report takes between 10 and 15 minutes. I would have another button (a 'Stop' button) that stops the previous process but doesn't stop my application. For ilustrate that, I'll show a simply code I can take as reference for resolve my app. I would like counting started by pressing at 'count' button, stops if I press 'stop' button.
ui.R code:
shinyUI(
fluidPage(
actionButton("count","Start count"),
actionButton("stop","Stop count")
)
)
server.R code:
shinyServer(function(input, output, session) {
observeEvent(input$count, {
observeEvent(input$stop, {
# Code for stop counting
})
i <- 1
for (i in i:10000) {
print(paste("Number: ",i))
}
})
})
Thanks a lot friends!
I found a very nice solution here: https://www.r-bloggers.com/2018/07/long-running-tasks-with-shiny-challenges-and-solutions/
library(shiny)
library(promises)
library(future)
plan(multiprocess)
ui <- fluidPage(
titlePanel("Long Run Stoppable Async"),
sidebarLayout(
sidebarPanel(
actionButton('run', 'Run'),
actionButton('cancel', 'Cancel'),
actionButton('status', 'Check Status')
),
mainPanel(
tableOutput("result")
)
)
)
server <- function(input, output) {
N <- 10
# Status File
status_file <- tempfile()
get_status <- function(){
scan(status_file, what = "character",sep="\n")
}
set_status <- function(msg){
write(msg, status_file)
}
fire_interrupt <- function(){
set_status("interrupt")
}
fire_ready <- function(){
set_status("Ready")
}
fire_running <- function(perc_complete){
if(missing(perc_complete))
msg <- "Running..."
else
msg <- paste0("Running... ", perc_complete, "% Complete")
set_status(msg)
}
interrupted <- function(){
get_status() == "interrupt"
}
# Delete file at end of session
onStop(function(){
print(status_file)
if(file.exists(status_file))
unlink(status_file)
})
# Create Status File
fire_ready()
nclicks <- reactiveVal(0)
result_val <- reactiveVal()
observeEvent(input$run,{
# Don't do anything if analysis is already being run
if(nclicks() != 0){
showNotification("Already running analysis")
return(NULL)
}
# Increment clicks and prevent concurrent analyses
nclicks(nclicks() + 1)
result_val(data.frame(Status="Running..."))
fire_running()
result <- future({
print("Running...")
for(i in 1:N){
# Long Running Task
Sys.sleep(1)
# Check for user interrupts
if(interrupted()){
print("Stopping...")
stop("User Interrupt")
}
# Notify status file of progress
fire_running(100*i/N)
}
#Some results
quantile(rnorm(1000))
}) %...>% result_val()
# Catch inturrupt (or any other error) and notify user
result <- catch(result,
function(e){
result_val(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(){
fire_ready()
nclicks(0)
})
# Return something other than the promise so shiny remains responsive
NULL
})
output$result <- renderTable({
req(result_val())
})
# Register user interrupt
observeEvent(input$cancel,{
print("Cancel")
fire_interrupt()
})
# Let user get analysis progress
observeEvent(input$status,{
print("Status")
showNotification(get_status())
})
}
# Run the application
shinyApp(ui = ui, server = server)