ric*_*rey 5 r shiny r-future r-promises
我正在尝试创建一个闪亮的下载处理程序,但使用 future_promise() 因为写入文件可能需要一些时间。这是我想做的一个工作示例,但不使用异步框架:
一个有效的 .Rmd 闪亮应用程序:当您单击按钮时,它会将 10 个随机偏差写入文件并提供下载。我添加了 5 秒的延迟。
---
title: "download, no futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
This version works.
```{r}
renderUI({
button_reactive <- reactive({
y = rnorm(10)
Sys.sleep(5)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
})
output$button <- downloadHandler(
filename = function() {
button_reactive() %>%
`[[`('fn')
},
content = function(f) {
d = button_reactive() %>%
`[[`('d')
con = file(description = f, open = "wb")
writeBin(object = d, con = con)
close(con)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
Run Code Online (Sandbox Code Playgroud)
我正在尝试使用 future_promise 在异步框架中实现这一点。这是 {future}/{promises} 版本:
---
title: "download futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(future)
library(promises)
plan(multisession)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
This version yields this error on download attempt, reported in the R console:
```
Warning: Error in enc2utf8: argument is not a character vector
[No stack trace available]
```
```{r}
renderUI({
button_reactive <- reactive({
future_promise({
y = rnorm(10)
Sys.sleep(5)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
}, seed = TRUE)
})
output$button <- downloadHandler(
filename = function() {
button_reactive() %...>%
`[[`('fn')
},
content = function(f) {
con = file(description = f, open = "wb")
d = button_reactive() %...>%
`[[`('d') %...>%
writeBin(object = ., con = con)
close(con)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
Run Code Online (Sandbox Code Playgroud)
当我在 Firefox 中单击按钮时,我没有得到任何文件,并且在 R 控制台中显示:
Warning: Error in enc2utf8: argument is not a character vector
[No stack trace available]
Run Code Online (Sandbox Code Playgroud)
经过一些调试,我相信这是因为运行下载处理程序的任何东西都在运行该filename
函数,期待一个字符向量,并得到一个承诺。但我不确定如何解决这个问题。
我看到了这个问题,其中提问者似乎有同样的问题,但没有提供解决方案(他们的例子不可重现)。
我怎样才能解决这个问题?
Promise 可以与 R Markdown 一起使用,但有一些好消息和坏消息。
好消息
Promise 在 downloadHandler 上工作
简而言之,Promise 可以用来代替返回值:它只是在稍后某个时间点提供的输出值。因此对于任何输出对象,包括downloadHandler
,您可以提供承诺而不是输出值。
Promise 由一个future_promise()
函数组成,该函数执行一些运行缓慢的操作(通常在不同的 R 会话中)和一个解析部分(跟随运算符的部分%...>%
),该部分获取结果并提供解析。两者的结合就是promise
.
它downloadHandler
有点特殊,因为它不接收对象作为输出,而是期望将同名文件f
写入磁盘(因此NULL
返回值)。您的原始代码返回了 a close(con)
,这是使代码正常工作的障碍(但不是错误的原因)。
为了让 Promise 在 上工作downloadHandler
,写入磁盘的文件必须被 Promise 替换。然而,在您的代码中,您的最后一行是close(con)
,这不是一个承诺。因此,首要任务是将文件写入卸载到函数,然后该函数可以成为未来构造的解析部分。
downloadHandler
正如@Waldi 所提到的,似乎不支持该filename
部分的承诺。我没有这方面的支持信息。
坏消息
Promise 在 R markdown 上下文中没有多大意义
正如本文中所解释的,Promise 可以在 Shiny 上下文中使用,并防止跨会话锁定服务器。在单个会话中,事件循环会等待所有承诺在渲染输出之前解决,这会导致我们都喜欢的同样卡住的 UI。只有当第二个会话处于活动状态时,才会承诺产生任何性能优势。
使用 downloadHandler 和 Promise 的完整示例
下面的代码是上面代码的改编版,有三个小区别:
downloadHandler
filename
参数现在是静态的downloadHandler
content
论证提供了充分的承诺保留序言
---
title: "download futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(future)
library(promises)
plan(multisession)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
Run Code Online (Sandbox Code Playgroud)
为了更加清晰,定义两个独立的函数。请注意,writeFile
这里负责处理所有 I/O,包括关闭连接
```{r}
createFile = function(){
y = rnorm(10)
Sys.sleep(1)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
}
writeFile = function (fut, f){
x = fut[['d']]
con = file(description = f, open = "wb")
writeBin(object = x, con = con)
close(con)
}
```
Run Code Online (Sandbox Code Playgroud)
UI 部分:请注意,内容现在返回一个承诺。
```{r}
renderUI({
testPromise = reactive({
future_promise({createFile()}, seed=T) %...>% (function (x) (x))()
})
fileName = reactive({
testPromise() %...>% '[['('fn')
})
output$button <- downloadHandler(
filename = function() {
'test.txt'
# This doesn't work - filename apparently doesn't support promises
# fileName()
},
content = function(f) {
# Content needs to receive promise as return value, so including resolution
testPromise() %...>% writeFile(., f)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
```
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
162 次 |
最近记录: |