我有一个简单的 Shiny 应用,其中数据集在调用中创建reactive()
,然后进行绘图。由于数据创建可能需要一段时间,我希望将其调用为异步,以便会话可以用于其他用途。为此,我想使用shiny::ExtendedTask()
with mirai
。
官方文档中有task$invoke(...)
由按钮触发的示例(例如在 内部shiny::observeEvent()
),但没有示例说明如果我的功能在 内部触发,我该如何构造它reactive()
。
请注意,我的实际用例在特定页面上的几个图中重复使用了结果数据。
同步应用程序 MWE
以下是阻止应用程序的 MWE
library(shiny)
library(bslib)
create_data <- function(n) {
Sys.sleep(2)
data.frame(x = seq(n), y = rnorm(n))
}
ui <- page_fillable(
card(
card_header(span("To show reactive state, here is the current time ",
textOutput("current_time", inline = TRUE))),
plotOutput("myplot")
)
)
server <- function(input, output, session) {
# note this reactive here should be put into an ExtendedTask
data <- reactive({
create_data(100)
})
output$myplot <- renderPlot({
d <- data()
plot(d$x, d$y)
})
# to show if the session is free: show current time
output$current_time <- renderText({
invalidateLater(1000, session)
format(Sys.time(), "%H:%M:%S")
})
}
shinyApp(ui, server)
create_data()
请注意,由于该功能阻止了会话,因此时间不会立即显示。
尝试异步应用程序
reactive()
为了显示事件触发的时间,我将内容移到了第二个导航栏页面。因此,我希望任务在页面显示时(例如,而不是observe()
行为)需要数据时触发。
我的失败尝试如下(请注意我是如何使用触发task$invoke
的,observe()
这是错误,但我不知道如何使用reactive()
或以其他方式触发它。
library(shiny)
library(bslib)
library(mirai)
create_data <- function(n) {
Sys.sleep(2)
data.frame(x = seq(n), y = rnorm(n))
}
# small helper function for logging
flog <- function(m) cat(sprintf("INFO [%s] | %s\n", format(Sys.time(), "%Y-%m-%d %H:%M:%OS3"), m))
ui <- page_navbar(
nav_panel("Empty Default"),
nav_panel(
"Same as Before",
card_header(span("To show reactive state, here is the current time ",
textOutput("current_time", inline = TRUE))),
plotOutput("myplot")
)
)
server <- function(input, output, session) {
# create the task
task <- ExtendedTask$new(function(...) mirai(fun(n = n), fun = create_data, ...))
# this is the error here: the observe is not triggered by the rendered plot but by observe => fires immediately
observe({
flog("Task Invoke")
task$invoke(n = 100)
flog("Task Invoke Done")
})
output$myplot <- renderPlot({
flog("Task Result")
data <- task$result()
flog("Task Result Done")
plot(data$x, data$y)
})
# to show if the session is free: show current time
output$current_time <- renderText({
invalidateLater(1000, session)
format(Sys.time(), "%H:%M:%S")
})
}
shinyApp(ui, server)
您可以将任务调用放入
reactive()
其自身的响应式中,以惰性触发。结果会被缓存,且永远不会失效,因此它只会运行一次。然后只需在输出中依赖该响应式即可:其结果为: