Like shiny::reactive(), but expr is evaluated on a mirai daemon so the
session stays responsive. Reactive reads that appear in expr, such as
input$x, input[["x"]], rv$y, data() or mod$data(), are evaluated on the session
thread, where they register dependencies as usual, and their values are sent
to the worker. Calls to req(), validate(), invalidateLater() and
isolate() also run on the session thread, unless they use variables
created inside expr or may not run at all; see Limits below.
Usage
reactiveAsync(
expr,
mode = c("latest", "queue"),
globals = NULL,
label = NULL,
env = parent.frame(),
quoted = FALSE
)Arguments
- expr
An expression.
- mode
"latest"(default) cancels the running task when a dependency changes."queue"runs tasks in order, like shiny::ExtendedTask.- globals
A named list of extra objects to make available to
exprin the worker.- label
A label for debugging.
- env
The environment in which to evaluate
expr.- quoted
Is
expra quoted expression?
Value
A reactive expression of class "reactiveAsync". Use it with
status_async(), has_value_async(), cancel_async()
and shiny::bindEvent().
Details
The task starts the first time the reactive or its status_async() is read,
so a spinner driven by the status is enough to kick it off. An output in a
hidden tab is suspended and does not read, so a task only it depends on
starts when the tab is opened; use shiny::outputOptions() with
suspendWhenHidden = FALSE to start it on load. While a task runs, reading
the reactive returns the previous value, or halts silently like req()
when there is none yet. Errors raised in the worker are re-thrown when the
reactive is read.
User-defined functions and objects that expr refers to (and functions they
call) are sent along automatically. Packages that are attached in the
session and used by expr are attached in the worker. print() shows the
split: the calls evaluated on the session, the objects and packages shipped
to the worker, and the rewritten worker expression, without evaluating
anything or starting the task. In shiny::devmode() each async reactive
prints itself on its first run; options(shinyasync.print = FALSE) turns
that off.
An async reactive can read another one, and the chain stays consistent as
a chain of shiny::reactive() expressions does: while an upstream task is
catching up with its own inputs, the downstream keeps its previous value
instead of combining a new input$x with an upstream value computed from
the old one, reports "running" while the upstream runs, and re-runs once
the upstream has settled. In mode = "latest" a downstream run already
computing on the old upstream value is dropped; in mode = "queue" it
completes. Staleness follows the chain, and an upstream error propagates
down it. A downstream gated with shiny::bindEvent() whose event fires
while its upstream is running runs once the upstream settles, as a
synchronous reactive() would have been ready at the click; if the
upstream is stale but not running, the event is dropped. The one
difference from reactive() is an upstream gated with bindEvent(): a
downstream that also reads one of the upstream's inputs waits for the next
event rather than recomputing with the old upstream value.
Limits
Reactive reads must appear literally in expr; reads inside helper
functions or with computed names (input[[paste0("x", i)]]) cannot be
resolved. Read such values in a separate shiny::reactive() first, or pass
them with globals.
Every reactive read in expr is a dependency, even one in a branch that is
not taken: if (input$a) input$b else 0 re-runs when input$b changes.
Wrap a read in shiny::isolate() to use its value without depending on it.
req() and validate() run on the session only when they are certain to
run, i.e. not inside if, &&, ||, switch, loops or functions;
otherwise they run in the worker, where a failure keeps the previous value.
Reads inside formulas are hoisted too, so lm(y ~ input$x) fits the model
on the value of input$x under a placeholder name. Other formula symbols
are shipped to the worker if they name a plain object in the session.
Assigning to reactive values (rv$x <- ...) inside expr is an error;
do that in an shiny::observe() that reads the result.
Examples
if (interactive()) {
library(shiny)
server <- function(input, output, session) {
r <- reactiveAsync({
Sys.sleep(1)
rnorm(input$n)
})
output$p <- renderPlot(hist(r()))
}
ui <- fluidPage(sliderInput("n", "n", 10, 1000, 100), plotOutput("p"))
shinyApp(ui, server)
}
