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)
Related
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 shiny apps, it can be difficult to use iterative self-referring.
This caused me a great deal of head scratching, but ...
Your proposed solution is not the correct thing to do in shiny, I strongly recommend against doing that. Do not use <<- for assignment, it is almost never the correct thing to do in shiny, it may "seem" right but has unintended behaviour that can lead to weird bugs. Usually when you want to use <<-, the correct thing to do is to use reactiveVal().
Consider the app below that uses <<- to save running sum of a number (I'm using a different sample app than yours to make it simpler and easier to understand):
ui <- fluidPage(
actionButton("add1", "Add 1"),
actionButton("add5", "Add 5")
)
server <- function(input, output) {
mysum <<- 0
observeEvent(input$add1, {
mysum <<- mysum + 1
print(mysum)
})
observeEvent(input$add5, {
mysum <<- mysum + 5
print(mysum)
})
}
shinyApp(ui, server)
The above app will seem to work fine when you test it, but when you put it out in the real world you would notice a bug: if you open two browser tabs, the variable is shared between the two. In fact, the number will be shared across all users. This won't happen in your app because your initial assignment used <- while the second one uses <<-, but the above pattern is something I see very often so I wanted to explicitly show it. The other thing that is wrong here is that now you've left the reactivity world - mysum is not a reactive variable, so we can't use it in any reactive context in shiny (this is why your non-solution #2 didn't work). Below is the better approach:
ui <- fluidPage(
actionButton("add1", "Add 1"),
actionButton("add5", "Add 5")
)
server <- function(input, output) {
mysum <- reactiveVal(0)
observeEvent(input$add1, {
mysum(mysum() + 1)
})
observeEvent(input$add5, {
mysum(mysum() + 5)
})
observe({
print(mysum())
})
}
shinyApp(ui, server)
You can read this answer for a bit more in depth discussion
Im posting this to help if anyone else has problems trying to carry a value over from one 'iteration' to the next in a shiny app.
In my case, I need to calculate a running mean.
library(shiny)
# UI
{
ui <- fluidPage(
textOutput('serial')
)
}
server <- function(input, output, session) {
serialStream <- reactive({
invalidateLater(10, session)
return(sample(1:100,1))
})
# Non-solution #1
# runningMean <- reactive({
# isolate(runningMean()*0.4) + serialStream()*0.1
# })
# output$serial <- renderText(runningMean())
# Non-solution #2
# runningMean <- 0 # initial value
# observe({
# runningMean <<- runningMean*0.4 + serialStream()*0.1
# })
# output$serial <- renderText(runningMean)
# Solution
rm <- 0 # initial value
runningMean <- reactive({
rm <<- rm * 0.4 + serialStream()*0.1
rm
})
output$serial <- renderText(runningMean())
}
# Run the application
shinyApp(ui = ui, server = server)
Something similar can be accomplished using observe(), but I ran into problems with memory leakage. The code above does the trick for me... Note the use of <<- instead of <- inside the reactive function. This ensures the value of rm is preserved outside that function.
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)
Goal and Current Approach
I have a module, which can delete its own UI. I also want to be able to call this killing functionality from outside the module, hence I pass an additional reactive to the module's server logic and call the killing routine when this reactive fires.
So far so good. Now I want to implement a function which first kills all instances of my module and then adds new instances of this module and this is where I struggle with the design I have chosen.
Issue
What I would need is that before my input$add5 observer adds any new instances, all old instances are killed. This would be done by setting kill_switch(TRUE), but the problem is that before the observer in my module can react to that, the input$add5 observer continues to add new modules, which reset the kill_switch to FALSE and basically the old instances are never killed.
Basically, in the current design I would like that the observer on kill_switch reacts immediately after I change the flag and only after this is done, my add5 observer continues.
What I need
I think that my design is not optimal, thus any recommendations of how to set up the interface between the main application and the module would be highly appreciated.
Code
library(shiny)
boxer_ui <- function(id) {
ns <- NS(id)
div(
id,
id = ns("killme"),
style = "background-color:steelblue; font-size: xx-large; color: white")
}
boxer <- function(input, output, session, kill_switch) {
ns <- session$ns
observe({
req(kill_switch())
removeUI(paste0("#", ns("killme")))
})
}
ui <- fluidPage(actionButton("new", "new"),
actionButton("killall", "Kill All"),
actionButton("add5", "Kill All & Add 5"),
fluidRow(id = "content"))
server <- function(input, output, session) {
ids <- reactiveVal(0)
kill_switch <- reactiveVal(FALSE)
handler <- reactiveValues()
add_new <- function() {
kill_switch(FALSE)
ids(ids() + 1)
new_id <- paste0("id", ids())
insertUI("#content", "beforeEnd", boxer_ui(new_id))
handler[[new_id]] <- callModule(boxer, new_id, kill_switch)
}
observeEvent(input$new, {
isolate({
add_new()
})})
observeEvent(input$add5, {
isolate({
kill_switch(TRUE)
replicate(5, add_new())
})})
observeEvent(input$killall, kill_switch(TRUE))
}
shinyApp(ui, server)
One solution I could think of is to split remove / add as follows:
server <- function(input, output, session) {
ids <- reactiveVal(0)
kill_switch <- reactiveVal(FALSE)
add5 <- reactiveVal(FALSE)
handler <- reactiveValues()
add_new <- function() {
kill_switch(FALSE)
ids(ids() + 1)
new_id <- paste0("id", ids())
insertUI("#content", "beforeEnd", boxer_ui(new_id))
handler[[new_id]] <- callModule(boxer, new_id, kill_switch)
}
observeEvent(input$new, {
isolate({
add_new()
})})
observeEvent(input$add5, {
isolate({
kill_switch(TRUE)
add5(TRUE)
})})
observe({
req(add5())
isolate({
replicate(5, add_new())
add5(FALSE)
})
})
observeEvent(input$killall, kill_switch(TRUE))
}
This is however based on the assumption that the observer will never be interrupted by any other observer. Is that true? In this case I could also add priority parameters to ensure that the inner observer is fired first.
Can anybody conform that my assumption is right?
I have a problem with my code. I have 2 input files which I want to read with click of button and a numeric input which contains a filter value for the output of the table being created from the 2 files (after manipulating the data). The whole process (read files + create table + filter) right now is executed every time the user click the button. I want to do only the filter action if the input files doesn't change, because the process takes long time.
After the first click I want to do only the filtering command when the numeric input changes, unless the input files is also changed by the user.
The following code reproduces my problem:
library(shiny)
library(data.table)
server <- function(input, output, session) {
output$table1 <- renderDataTable({
input$gobtn
isolate({
infile1 <<- input$f1
infile2 <<- input$f2
if (is.null(infile1) || is.null(infile1)) {
return (NULL)
}
else {
calc()
}
})
})
calc <- function() {
inf1 <<- fread(infile1$datapath)
inf2 <<- fread(infile2$datapath)
# do some process with files data.....
my_table <- as.data.table(rbind(inf1, inf2))
setnames(my_table, c('name', 'rank'))
result <- my_table[rank > input$rank]
return(result)
}
}
ui <- basicPage(
fileInput("f1", "f1"),
fileInput("f2", "f2"),
numericInput("rank", "show rank only above :", value = 6),
actionButton("gobtn", "show"),
dataTableOutput('table1')
)
shinyApp(ui = ui, server = server)
The way to use reactivity is to break things into parts, so that you only need to update what is necessary. The first step in your pipeline is reading and processing the files. This seems like a good reactive: if they don't change, nothing happens, but when they change, everything that needs to be recalculated is recalculated. The next step is filtering, when the filter variable changes we want to refilter the data. Then we can just put that in the output.
server <- function(input, output, session) {
processedData <- reactive({
req(input$f1,input$f2)
inf1 <- fread(input$f1$datapath)
inf2 <- fread(input$f2$datapath)
# do some process with files data.....
my_table <- as.data.table(rbind(inf1, inf2))
setnames(my_table, c('name', 'rank'))
my_table
}
filteredData <- reactive({
req(input$rank)
processedData()[processedData()$rank > input$rank]
})
output$table1 <- renderDataTable({
input$gobtn
isolate({
filteredData()
})
})
}