有没有办法在 Shiny 中构建地块时显示地块,而不是等待所有地块?

Is there a way to display plots as they build in Shiny instead of waiting for all of them?

我有一个闪亮的仪表板,上面有很多图,所有这些都需要几秒钟的时间来构建。一旦构建了最后一个,它们就会全部显示出来。相反,我希望每个图在完成后立即显示。我知道 R 是单线程的,但似乎必须有一种方法可以达到 "return execution to the display code" 或类似的东西。

这段代码演示了这个问题:

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)

休眠只是为了模拟慢速执行。

页面显示需要40秒。我希望页面用 10s 来显示 plot1,然后再花 10s 来显示 plot2,等等。是否有像 UpdatePage() 这样的调用可以在每个 plot 函数的底部调用?

在我的页面上,我有加载动画 运行 这样用户就知道 activity 正在发生,这使得他们在一次加载时更加明显。

当然,我可以有一个更简单的页面,但那样它就不是仪表板了。 :)

您可以使用 reactiveTimer() 定期刷新您的页面。

并且您可以将绘图保存在 list 个绘图中,以便在每次刷新时立即打印它们。

我不得不重新排序 renderPlot 函数,这样 step 迭代器一次只渲染一个图

此外,我选择不立即开始第一个渲染来绘制 "Loading" 图。

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)