The generative art example from the Shiny app
vignette shows a simple approach to asynchronous Shiny apps which
leverages Shiny extended tasks and crew
promises. The
example app relies on Shiny extended tasks and crew
promises. This vignette explains how promises work in
crew
.1
crew
A crew
controller can generate two types of promise
objects for use with the promises
package:
tibble
of results and
metadata. On error, task is still asynchronously popped, but the error
message of the task is returned instead.tibble
of all results and metadata (with one
row per task). On error, tasks are all still asynchronously popped, but
the error message of one of the tasks is returned instead.To dive into single-task promises, let’s start a local controller first.
library(crew)
library(dplyr)
library(promises)
controller <- crew_controller_local(workers = 2L)
controller$start()
Let’s push a single task.
To create a promise specific to the task above, call
as.promise()
on the returned task object, then call
controller$autoscale()
to tell the main event loop to
periodically launch any necessary crew
workers.
To create a promise that resolves when any task in the
controller completes, use the promise()
method of the
controller. controller$promise()
always calls
controller$autoscale()
behind the scenes, so there is no
need to call it manually in this case. The following promise prints the
output value asynchronously if the task succeeds.
promise <- controller$promise(mode = "one") %...>%
mutate(result = as.character(result)) %...>%
print()
When you run both steps above, the R interpreter runs it immediately and returns control back to you. But then the following output prints two seconds after the task was pushed.
#> # A tibble: 1 × 12
#> name command result seconds seed algorithm error trace
#> <chr> <chr> <chr> <dbl> <int> <chr> <chr> <chr>
#> 1 success "{\n Sy… done 2.00 NA NA NA NA
#> # ℹ 4 more variables: warnings <chr>, launcher <chr>,
#> # worker <int>, instance <chr>
The task below runs in the background for 2 seconds and then throws an error.
As before, control returns immediately when you push the task and create the promise.
promise <- then(
controller$promise(mode = "one"),
onRejected = function(error) {
print(conditionMessage(error))
}
)
But this time, an error message prints two seconds later.
To demonstrate multi-task promises, we push multiple tasks at once.
The walk()
method is like map()
, except that
it returns control immediately without waiting for any tasks to
complete.
controller$walk(
command = {
Sys.sleep(2)
argument
},
iterate = list(argument = c("x", "y")),
names = "argument",
save_command = TRUE
)
We create a promise which asynchronously resolves when all the tasks in the controller finish.
promise <- controller$promise(mode = "all") %...>%
mutate(result = as.character(result)) %...>%
select(any_of(c("name", "command", "result", "error", "worker"))) %...T>%
print()
Two seconds after walk()
was called, the promise
resolves asynchronously and prints the results of all the tasks. Each
row in the tibble
below corresponds to an individual
task.
#> # A tibble: 2 × 5
#> name command result error worker
#> <chr> <chr> <chr> <chr> <int>
#> 1 x "{\n Sys.sleep(2)\n argument\n}" x NA 1
#> 2 y "{\n Sys.sleep(2)\n argument\n}" y NA 2
A couple remarks:
walk()
with multi-task promises.
You can push tasks individually and still create a promise which
resolves they all finish.To demonstrate (1) and (2), let’s push a task that will succeed and a task that will throw an error.
controller$push(
name = "success",
command = {
Sys.sleep(2)
"done"
},
save_command = TRUE
)
controller$push(
name = "error",
command = {
Sys.sleep(2)
stop("one task's error message")
},
save_command = TRUE
)
We create a multi-task promise which prints the error message asynchronously on resolution.
promise <- then(
controller$promise(mode = "all"),
onRejected = function(error) {
print(conditionMessage(error))
}
)
Two seconds after the tasks were pushed, the error message prints.
For general information on promises in R, please visit https://rstudio.github.io/promises/.↩︎