【问题标题】:In Shiny apps for R, how do I delay the firing of a reactive?在 R 的 Shiny 应用程序中,如何延迟触发反应?
【发布时间】:2015-09-18 14:43:25
【问题描述】:

我的Shiny 应用程序中有一个selectizeInput。它处于多选模式,因此用户可以指定多个选择。

但是,每次添加选择时,依赖于 selectizeInput 的反应器都会被触发。假设用户打算选择ABC。目前,当只需要最后一个时,我的应用程序将对AA, BA, B, C 进行昂贵的计算。

我能想到的解决这个问题的最佳方法是将selectizeInput 的触发延迟一秒左右,让用户有机会输入所有选择。每个新选择都应将计时器设置回 1 秒。我知道Shiny 提供了一个invalidateLater 命令,但这会导致反应器现在和以后触发一次。

我怎样才能让响应式稍后触发一次?

【问题讨论】:

  • 你能用“Go!”吗?按钮,所以只有在用户选择了所有内容后才会触发计算?
  • 这或多或少是我回答的stackoverflow.com/questions/31051133/… 的副本。简而言之,您将反应值与计时器结合起来。
  • 使用操作按钮绝对是要走的路。如果您选择预定事件并且用户“放慢”速度,那么您将回到原点。
  • 按照@MarkeD 和 zero323 以及 .您可以将计算存储为索引以监视哪些已经计算,并且仅在 selectizeInput 变量不是 %in% 已经计算的变量时才计算新的
  • @sdgfsdh 实际上这不是我的代码所做的。它不会像正常情况那样轮询输入。当一个输入被触发时,而不是更新输出,一个定时器被启动。如果在计时器结束之前触发了另一个输入,则重置计时器。否则,当计时器经过时,输出会更新。如果您想检查一下,请插入一些 catprint 语句并观察控制台。这似乎就是你在这里所追求的。如果不是,也许你可以详细说明。不过,总的来说,我认为按钮可能是要走的路。

标签: r timer delay shiny reactive-programming


【解决方案1】:

你应该 debounce 响应式。

这里有一个 R 实现: https://gist.github.com/jcheng5/6141ea7066e62cafb31c

# Returns a reactive that debounces the given expression by the given time in
# milliseconds.
#
# This is not a true debounce in that it will not prevent \code{expr} from being
# called many times (in fact it may be called more times than usual), but
# rather, the reactive invalidation signal that is produced by expr is debounced
# instead. This means that this function should be used when \code{expr} is
# cheap but the things it will trigger (outputs and reactives that use
# \code{expr}) are expensive.
debounce <- function(expr, millis, env = parent.frame(), quoted = FALSE,
  domain = getDefaultReactiveDomain()) {
  
  force(millis)
  
  f <- exprToFunction(expr, env, quoted)
  label <- sprintf("debounce(%s)", paste(deparse(body(f)), collapse = "\n"))

  v <- reactiveValues(
    trigger = NULL,
    when = NULL # the deadline for the timer to fire; NULL if not scheduled
  )  

  # Responsible for tracking when f() changes.
  observeEvent(f(), {
    # The value changed. Start or reset the timer.
    v$when <- Sys.time() + millis/1000
  }, ignoreNULL = FALSE)

  # This observer is the timer. It rests until v$when elapses, then touches
  # v$trigger.
  observe({
    if (is.null(v$when))
      return()
    
    now <- Sys.time()
    if (now >= v$when) {
      v$trigger <- runif(1)
      v$when <- NULL
    } else {
      invalidateLater((v$when - now) * 1000, domain)
    }
  })

  # This is the actual reactive that is returned to the user. It returns the
  # value of f(), but only invalidates/updates when v$trigger is touched.
  eventReactive(v$trigger, {
    f()
  }, ignoreNULL = FALSE)
}


#' @examples
#' library(shiny)
#' 
#' ui <- fluidPage(
#'   numericInput("val", "Change this rapidly, then pause", 5),
#'   textOutput("out")
#' )
#' 
#' server <- function(input, output, session) {
#'   debounced <- debounce(input$val, 1000)
#'   output$out <- renderText(
#'     debounced()
#'   )
#' }
#' 
#' shinyApp(ui, server)

【讨论】:

    猜你喜欢
    • 2013-12-27
    • 2023-03-18
    • 1970-01-01
    • 2018-10-24
    • 2023-01-11
    • 2017-05-31
    • 2019-08-14
    • 2015-01-23
    • 2019-10-28
    相关资源
    最近更新 更多