R 闪亮的承诺/未来阻止进程 [英] R Shiny promise/future blocks process
问题描述
尽管尝试使用异步 future
+ promises
策略,但我无法找出导致 Shiny 进程阻塞的脚本逻辑中的错误.
I cannot figure out the fault in my script's logic that causes Shiny process blocking despite trying to use async future
+ promises
strategy.
我有这个(简化的)服务器脚本.它原则上有效,但我没有经历并行化.我模拟了 2 个同时触发,第二个触发事件等待第一个事件解决.
I have this (simplified) server script. It works in principle, but I don't experience parallelization. I simulate 2 simultaneous triggering and the second triggering event waits until the first one resolves.
你能指出这里有什么问题吗?我已经阅读了好几本手册,但我仍然很难确定逻辑.
Can you please indicate what is wrong here? I have read several manuals, but I still find it hard to nail down the logic.
最小示例,单文件 Shiny 应用程序:
Minimal Example, a one-file Shiny app:
## load libs
library(shiny)
library(DT)
library(ggplot2)
ui <- navbarPage(
tabPanel(
"Новостные тренды"
, sidebarLayout(
sidebarPanel(
br()
, actionButton(
"run_trends"
, label = "run"
, style="color: #fff; background-color: #337ab7; border-color: #2e6da4"
)
, br()
)
, mainPanel(
textOutput("trends_time")
, br()
, br()
, plotOutput('trend_plotly')
, br()
, p("results")
, br()
, DTOutput('trend_tbl')
, br()
, br()
)
)
)
)
server <- function(input, output, session)
{
dt_trend <- observeEvent(
input$run_trends,
{
## load libs
library(data.table)
library(ggplot2)
library(promises)
library(future)
plan(multiprocess)
dat_func <- function()
{
start_time <- Sys.time()
dt <- data.table(x = rnorm(100), y = rnorm(100))
trendy_tbl <- head(dt, 10)
ggplo1 <- ggplot(dt) + geom_point(aes(x=x,y=y))
Sys.sleep(10)
list(
trendy_tbl
, ggplo1
, paste0('time: ', round(Sys.time() - start_time), ' сек.')
)
}
f <- future({
dat_func()
})
#res <- future::value(f)
output$trend_tbl <- renderDT({future::value(f)[[1]]})
output$trend_plotly <- renderPlot({future::value(f)[[2]]})
output$trends_time <- renderText({future::value(f)[[3]]})
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server, options = list(port = 4600, host = '0.0.0.0'))
推荐答案
试试这个:
library(data.table)
library(ggplot2)
library(promises)
library(future)
plan(multiprocess)
server <- function(input, output, session) {
dt_trend <- eventReactive(
input$run_trends,
{
dat_func <- function() {
start_time <- Sys.time()
dt <- data.table(x = rnorm(100), y = rnorm(100))
trendy_tbl <- head(dt, 10)
ggplo1 <- ggplot(dt) + geom_point(aes(x=x,y=y))
Sys.sleep(10)
list(
trendy_tbl
, ggplo1
, paste0('time: ', round(Sys.time() - start_time), ' сек.')
)
}
# Returning future
future({
dat_func()
})
})
output$trend_tbl <- renderDT({dt_trend()[[1]]})
output$trend_plotly <- renderPlot({dt_trend()[[2]]})
output$trends_time <- renderText({dt_trend()[[3]]})
}
关键思想是:
- 确保您使用引入了异步支持的
shiny
版本 > 1.1.0(2018 年 4 月). - 不要使用
future::value
,因为它会阻塞并等待未来,这正是我们想要避免的. - 相反,在
reactive
中返回未来.在这种情况下,这意味着使用eventReactive
而不是observeEvent
. - 通过反应式访问未来的价值.请注意,您的反应价值现在是未来!这意味着您需要使用未来的处理程序来使用该值.(*)
- 您还可以在任何
renderXXX
函数中返回一个 future.例如,如果您有大量耗时的绘图,则很有用.
- Make sure you use
shiny
version > 1.1.0 (April 2018) which introduced async support. - do NOT use
future::value
as it blocks and waits for the future, precisely what we want to avoid. - instead, return the future in a
reactive
. In this case, this means usingeventReactive
instead ofobserveEvent
. - Access the value of the future via the reactive. Note that your reactive value is now a future! This means you need to use future handlers to use the value. (*)
- You can also return a future in any
renderXXX
function. Useful for example if you have large time-consuming plots.
(*) 实际上,这意味着你需要做
(*) In practice, this means you need to do
renderDT(dt_trend() %>% then(~.[[1]]))
# or
renderDT(dt_trend() %>...% `[[`(1))
其中 then
来自 promises
包,[[
是基础 R (x[[i]]
实际上是`[[`(x, i)
!).
where then
is from the promises
package and [[
is the subsetting function from base R (x[[i]]
is actually semantic sugar for`[[`(x, i)
!).
在您的示例中,您基本上计算了 dt_trend
中单个 future
中的所有内容.您可能需要考虑使用多个小型期货.你可以在一个带有未来的反应式中加载数据,然后将输出代码保存在你的 renderXXX
函数中,如果需要的话,包装在 future
中.
In your example, you basically compute everything in a single future
in dt_trend
. You may want to consider using multiple small futures instead. You can load the data in a reactive with a future, then keep the output code in your renderXXX
functions, wrapped in a future
if needed.
通过运行 vignette("shiny", package = "promises")
(**),有一个关于使用具有闪亮可用的 Promise 的很好的小插图.它也可以在 cran 或rstudio 博客.
There is a good vignette on using promises with shiny available by running vignette("shiny", package = "promises")
(**). It is also available online on cran or on the rstudio blog.
(**) 如果您使用 install_github("rstudio/promises")
安装了 promise,您很可能必须先使用 build_vignettes = TRUE
重新安装.
(**) If you installed promises with install_github("rstudio/promises")
, you most likely have to re-install with build_vignettes = TRUE
first.
这篇关于R 闪亮的承诺/未来阻止进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!