You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Shiny中reactive()改为用ExtendedTask和mirai实现异步?

问题描述

我有一个简单的Shiny应用,原本在reactive()调用中创建数据集并绘图。由于数据创建可能耗时较长,我希望将其改为异步调用,以便会话可处理其他操作。为此,我想使用shiny::ExtendedTask搭配mirai。

官方文档中有task$invoke(...)通过按钮触发(例如在shiny::observeEvent()内)的示例,但没有说明如何在reactive()触发的场景下进行结构设计。我的实际场景需要在特定页面的多个绘图中复用生成的数据。

同步应用最小可复现示例(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("为展示响应状态,当前时间:",
                     textOutput("current_time", inline = TRUE))),
    plotOutput("myplot")
  )
)

server <- function(input, output, session) {
  # 此处的reactive需要替换为ExtendedTask
  data <- reactive({
    create_data(100)
  })
  
  output$myplot <- renderPlot({
    d <- data()
    plot(d$x, d$y)
  })

  
  # 展示会话是否空闲:实时更新当前时间
  output$current_time <- renderText({
    invalidateLater(1000, session)
    format(Sys.time(), "%H:%M:%S")
  })
  
}

shinyApp(ui, server)

注意由于create_data()函数会阻塞会话,时间不会立即更新显示。

异步应用尝试

为展示事件触发时机,我将内容移至第二个导航栏页面,期望在页面显示、数据需要时触发任务(即reactive()而非observe()的行为)。我的尝试未成功,当前用observe()触发task$invoke的方式会立即执行,而非由绘图渲染触发,示例代码如下:

library(shiny)
library(bslib)
library(mirai)

create_data <- function(n) {
  Sys.sleep(2)
  data.frame(x = seq(n), y = rnorm(n))
}
# 日志辅助函数
flog <- function(m) cat(sprintf("INFO [%s] | %s\n", format(Sys.time(), "%Y-%m-%d %H:%M:%OS3"), m))

ui <- page_navbar(
  nav_panel("默认空页面"),
  nav_panel(
    "与之前一致的页面",
    card_header(span("为展示响应状态,当前时间:",
                     textOutput("current_time", inline = TRUE))),
    plotOutput("myplot")
  )
)

server <- function(input, output, session) {
  # 创建任务
  task <- ExtendedTask$new(function(...) mirai(fun(n = n), fun = create_data, ...))
  
  # 问题所在:observe并非由绘图渲染触发,而是立即执行
  observe({
    flog("任务调用")
    task$invoke(n = 100)
    flog("任务调用完成")
  })
  
  output$myplot <- renderPlot({
    flog("获取任务结果")
    data <- task$result()
    flog("获取任务结果完成")
    
    plot(data$x, data$y)
  })
  
  
  # 展示会话是否空闲:实时更新当前时间
  output$current_time <- renderText({
    invalidateLater(1000, session)
    format(Sys.time(), "%H:%M:%S")
  })
}

shinyApp(ui, server)

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 07:57:36