jey1401
jey1401

Reputation: 371

Force rendering of already computed reactive elements

I'm trying to build a shiny app that outputs several results through different render* functions.

The problem is that one of those results takes some time to compute. So I would like shiny to render the quick results as soon as possible.

Here is some code to illustrate

# ui.R
library(shiny)

shinyUI(fluidPage(
    textOutput("res1"),
    textOutput('res2')
))

# server.R
library(shiny)

shinyServer(function(input, output) {

    output$res1 = renderText({
        "shows up instantly"
    })

    output$res2 = renderText({
            Sys.sleep(3)
            "shows up after 3 sec"
    })
})

For now, the webpage stays empty for 3 seconds and the two elements are rendered at once.

My question is the following one: is it possible to enforce that output$res1 executes before output$res2 and that it sends its results to the browser before the long computation begins ?

Upvotes: 5

Views: 2512

Answers (2)

jey1401
jey1401

Reputation: 371

I found a workaround. The idea is to force all render* functions to send their results to the browser once before launching the long computations.

In the following code, the two text zones appear immediately and the second one is updated after 3 seconds.

shinyServer(function(input, output,session) {

    status=reactiveValues(res1IsDone=FALSE,res2HasRendered=FALSE)

    output$res1 = renderText({
        status$res1IsDone = TRUE
        "shows up instantly"
    })

    output$res2 = renderText({
        if(isolate(!status$res1IsDone || !status$res2HasRendered)) {
            status$res2HasRendered = TRUE
            invalidateLater(100,session)
            "wait"
        } else {
            Sys.sleep(3)
            "shows up after 3 sec"

        }
    })
})

To my understanding, shiny is monothreaded and the results are sent back to the browser once all the render* functions are executed once (or when all invalidation are resolved ?).

Upvotes: 2

RmIu
RmIu

Reputation: 4477

Check out invalidateLater otherwise if you only want to render text you can send text directly to the client using:

# ui.R
library(shiny)

ui <- shinyUI(fluidPage(
  tags$head(
    tags$script(
      HTML("
            Shiny.addCustomMessageHandler ('print',function (message) {
              $('#'+message.selector).html(message.html);
              console.log(message);
            });
           ")
    )
  ),
  textOutput("res1"),
  textOutput('res2')
))

# server.R
server <- shinyServer(function(input, output, session) {
  session$sendCustomMessage(type = 'print', message = list(selector = 'res1', html = "shows up instantly"))
  Sys.sleep(3)
  session$sendCustomMessage(type = 'print', message = list(selector = 'res2', html = "shows up after 3 sec"))
})

shinyApp(ui = ui, server = server)

Upvotes: 2

Related Questions