8.3 Progress bars

  • good for long-running tasks
  • you need to be able to divide the big task into a known number of small pieces that each take roughly the same amount of time

8.3.1 Shiny

Use withProgress() and incProgress()

ui <- fluidPage(
  numericInput("steps", "How many steps?", 10),
  actionButton("go", "go"),
  textOutput("result")
)

server <- function(input, output, session) {
  data <- eventReactive(input$go, {
    withProgress(message = "Computing random number", {
      for (i in seq_len(input$steps)) {
        Sys.sleep(0.5)
        incProgress(1 / input$steps)
      }
      runif(1)
    })
  })
  
  output$result <- renderText(round(data(), 2))
}

Check out this app: https://hadley.shinyapps.io/ms-progress

8.3.2 Waiter

  • waiter package uses an R6 object
ui <- fluidPage(
  waiter::use_waitress(),
  numericInput("steps", "How many steps?", 10),
  actionButton("go", "go"),
  textOutput("result")
)
server <- function(input, output, session) {
  data <- eventReactive(input$go, {
    # Create a new progress bar
    waitress <- waiter::Waitress$new(max = input$steps)
    # Automatically close it when done
    on.exit(waitress$close())
    
    for (i in seq_len(input$steps)) {
      Sys.sleep(0.5)
      # increment one step
      waitress$inc(1)
    }
    
    runif(1)
  })
  
  output$result <- renderText(round(data(), 2))
}

–> example app: https://shiny.john-coene.com/waiter/

8.3.3 Spinners

  • also use the waiter() package
  • instead of Waitress –> Waiter
ui <- fluidPage(
  waiter::use_waiter(),
  actionButton("go", "go"),
  textOutput("result")
)

server <- function(input, output, session) {
  data <- eventReactive(input$go, {
    waiter <- waiter::Waiter$new()
    waiter$show()
    on.exit(waiter$hide())
    
    Sys.sleep(sample(5, 1))
    runif(1)
  })
  output$result <- renderText(round(data(), 2))
}

Check out this app: https://hadley.shinyapps.io/ms-spinner-1

You can use Waiter for specific outputs, which will make the code simpler:

ui <- fluidPage(
  waiter::use_waiter(),
  actionButton("go", "go"),
  plotOutput("plot"),
)

server <- function(input, output, session) {
  data <- eventReactive(input$go, {
    waiter::Waiter$new(id = "plot")$show()
    
    Sys.sleep(3)
    data.frame(x = runif(50), y = runif(50))
  })
  
  output$plot <- renderPlot(plot(data()), res = 96)
}

Check out this app: https://hadley.shinyapps.io/ms-spinner-2

Check out all the available spinners: https://shiny.john-coene.com/waiter/

–> example app for more spinners