使用函数的反应性(在 Shiny 中)
Using reactivity (in Shiny) from a function
我正在为我的应用程序编写一个函数,它将在后台调用一个进程,并在它停止时进行监控。关闭会话后,进程应该被终止,所以我希望它在函数之外可用。如果我用 <<-
符号分配进程,会发生一些奇怪的事情...
library(shiny)
library(processx)
runAsync <- function() {
mainProcess <<- process$new("sleep", "10")
procTimer <- reactivePoll(1000, NULL,
checkFunc = function()
mainProcess$is_alive() ,
valueFunc = function()
mainProcess$is_alive()
)
observeEvent(procTimer(), {
if(procTimer()) {print("Begin")} else {print("End")}
print( mainProcess)
})
}
ui <- fluidPage(
actionButton("runBtn", "Run async process"),
)
server <- function(input, output, session) {
observeEvent(input$runBtn, runAsync())
}
shinyApp(ui, server)
第一次单击该按钮时,一切正常。输出是
[1] "Begin"
PROCESS 'sleep', running, pid #####.
[1] "End"
PROCESS 'sleep', finished.
但是第二次,observeEvent 被触发了两次。第三次,observedEvent 被触发了三次。以此类推...第三次点击按钮时的输出为
PROCESS 'sleep', running, pid 19228.
[1] "Begin"
PROCESS 'sleep', running, pid 19228.
[1] "Begin"
PROCESS 'sleep', running, pid 19228.
[1] "End"
PROCESS 'sleep', finished.
[1] "End"
PROCESS 'sleep', finished.
[1] "End"
PROCESS 'sleep', finished.
如果我用一个简单的 <-
赋值替换 <<-
赋值,就可以修复它(至少在表面上),问题就不会发生。
所以,我的主要问题是这种行为的原因是什么?我想了解。
我还想问一个附带的问题:这样的功能有什么好的设计?在函数内启动 reactivePoll 是好习惯吗?
问题是每次调用 runAsync()
时都会添加一个新的 observeEvent
调用,一个快速解决方法是引入一个反应值来检查该函数是否已被调用,然后再不调用添加一个新的 observeEvent
,如该示例所示:
library(shiny)
library(processx)
runAsync <- function(start) {
mainProcess <<- process$new("sleep", "10")
procTimer <- reactivePoll(1000, NULL,
checkFunc = function()
mainProcess$is_alive() ,
valueFunc = function()
mainProcess$is_alive()
)
if(isTRUE(reactive_value$start))
{
observeEvent(procTimer(), {
if(procTimer()) {print("Begin")} else {print("End")}
print( mainProcess)
})
reactive_value$start <- FALSE
}
}
ui <- fluidPage(
actionButton("runBtn", "Run async process"),
)
reactive_value <- shiny::reactiveValues(start = TRUE)
server <- function(input, output, session) {
observeEvent(input$runBtn, runAsync(reactive_value))
}
shinyApp(ui, server)
较长的解决方案是以 observeEvent
调用在 runAsync
函数之外的方式重新构建代码
我正在为我的应用程序编写一个函数,它将在后台调用一个进程,并在它停止时进行监控。关闭会话后,进程应该被终止,所以我希望它在函数之外可用。如果我用 <<-
符号分配进程,会发生一些奇怪的事情...
library(shiny)
library(processx)
runAsync <- function() {
mainProcess <<- process$new("sleep", "10")
procTimer <- reactivePoll(1000, NULL,
checkFunc = function()
mainProcess$is_alive() ,
valueFunc = function()
mainProcess$is_alive()
)
observeEvent(procTimer(), {
if(procTimer()) {print("Begin")} else {print("End")}
print( mainProcess)
})
}
ui <- fluidPage(
actionButton("runBtn", "Run async process"),
)
server <- function(input, output, session) {
observeEvent(input$runBtn, runAsync())
}
shinyApp(ui, server)
第一次单击该按钮时,一切正常。输出是
[1] "Begin"
PROCESS 'sleep', running, pid #####.
[1] "End"
PROCESS 'sleep', finished.
但是第二次,observeEvent 被触发了两次。第三次,observedEvent 被触发了三次。以此类推...第三次点击按钮时的输出为
PROCESS 'sleep', running, pid 19228.
[1] "Begin"
PROCESS 'sleep', running, pid 19228.
[1] "Begin"
PROCESS 'sleep', running, pid 19228.
[1] "End"
PROCESS 'sleep', finished.
[1] "End"
PROCESS 'sleep', finished.
[1] "End"
PROCESS 'sleep', finished.
如果我用一个简单的 <-
赋值替换 <<-
赋值,就可以修复它(至少在表面上),问题就不会发生。
所以,我的主要问题是这种行为的原因是什么?我想了解。
我还想问一个附带的问题:这样的功能有什么好的设计?在函数内启动 reactivePoll 是好习惯吗?
问题是每次调用 runAsync()
时都会添加一个新的 observeEvent
调用,一个快速解决方法是引入一个反应值来检查该函数是否已被调用,然后再不调用添加一个新的 observeEvent
,如该示例所示:
library(shiny)
library(processx)
runAsync <- function(start) {
mainProcess <<- process$new("sleep", "10")
procTimer <- reactivePoll(1000, NULL,
checkFunc = function()
mainProcess$is_alive() ,
valueFunc = function()
mainProcess$is_alive()
)
if(isTRUE(reactive_value$start))
{
observeEvent(procTimer(), {
if(procTimer()) {print("Begin")} else {print("End")}
print( mainProcess)
})
reactive_value$start <- FALSE
}
}
ui <- fluidPage(
actionButton("runBtn", "Run async process"),
)
reactive_value <- shiny::reactiveValues(start = TRUE)
server <- function(input, output, session) {
observeEvent(input$runBtn, runAsync(reactive_value))
}
shinyApp(ui, server)
较长的解决方案是以 observeEvent
调用在 runAsync
函数之外的方式重新构建代码