I have a Shiny Dashboard with many plots, all of which take multiple seconds to build. Once the last one builds, they all display. I would like instead for each plot to display as soon as it completes. I understand R is single-threaded, but it seems as though there must be a way to "return execution to the display code" or something like that.
This code demonstrates the issue:
library(shiny)
ui <- fluidPage(
title = "Page loading test"
, h1("Page loading test")
, plotOutput("plot1")
, plotOutput("plot2")
, plotOutput("plot3")
, plotOutput("plot4")
)
server <- function(input, output) {
output$plot1 <- renderPlot({
Sys.sleep(10)
plot(rnorm(50))
})
output$plot2 <- renderPlot({
Sys.sleep(10)
plot(rnorm(50))
})
output$plot3 <- renderPlot({
Sys.sleep(10)
plot(rnorm(50))
})
output$plot4 <- renderPlot({
Sys.sleep(10)
plot(rnorm(50))
})
}
shinyApp(ui = ui, server = server)
The sleeps are merely to emulate slow execution.
It takes 40s for the page to display. I would like the page to take 10s to display plot1, then an additional 10s to display plot2, etc. Is there a call like UpdatePage() that can be called at the bottom of each plot function?
On my page, I have loading animations running so the user is aware activity is occurring, which makes it even more obvious when they load at once.
I could, of course, have a simpler page, but then it wouldn't be a dashboard. :)
You can use reactiveTimer()
to refresh your page regularly.
And you can save your plots in a list
of plots to print them immediately at each refresh.
I had to reorder the renderPlot
functions so the step
iterator only renders one plot at a time
Also I chose not to start the first render right away to plot "Loading" plots.
library(shiny)
ui <- fluidPage(
title = "Page loading test"
, h1("Page loading test")
, plotOutput("plot1")
, plotOutput("plot2")
, plotOutput("plot3")
, plotOutput("plot4")
)
# Loading plot
plot(-1:1, -1:1, type = "n", xlab = "", ylab = "")
text(0,0, "Loading",cex = 5)
loading <- recordPlot()
plotlist <- vector("list",4)
step <- 0 # which plot should be rendered next
server <- function(input, output, session) {
autoInvalidate <- reactiveTimer(10, session)
output$plot4 <- renderPlot({autoInvalidate();
if(step>4){plotlist[[4]]}
else if(step==4){step <<- step+1
print("rendering step 4")
Sys.sleep(10)
plotlist[[4]] <<- {plot(rnorm(50));recordPlot()}} else loading
})
output$plot3 <- renderPlot({autoInvalidate();
if(step>3){plotlist[[3]]}
else if(step==3){step <<- step+1
print("rendering step 3")
Sys.sleep(10)
plotlist[[3]] <<- {plot(rnorm(50));recordPlot()}} else loading
})
output$plot2 <- renderPlot({autoInvalidate();
if(step>2){plotlist[[2]]}
else if(step==2){step <<- step+1
print("rendering step 2")
Sys.sleep(10)
plotlist[[2]] <<- {plot(rnorm(50));recordPlot()}} else loading
})
output$plot1 <- renderPlot({autoInvalidate();
if(step>1){plotlist[[1]]}
else if(step==1){step <<- step+1
print("rendering step 1")
Sys.sleep(10)
plotlist[[1]] <<- {plot(rnorm(50));recordPlot()}} else {step <<-1;loading}
})
}
shinyApp(ui = ui, server = server)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With