Interrupting a background process
I had been running a shiny app in which I could halt a long-running process. The process would update the shiny app and constantly looked for an updated shiny variable to inform if a cancel button had been pressed. The code had been adapted from a SO post at https://stackoverflow.com/questions/30587883/is-it-possible-to-stop-executing-of-r-code-inside-shiny-without-stopping-the-sh/34517844#34517844. Unfortunately this utilized the httpuv::service function in a manner for which it was not designed. At some point, when shiny and httpuv were updated, this functionality ceased to work. Thanks to some help from Joe Cheng a similar functionality can be derived using an asynchronous function. A working example is shown below.
1library(shiny)
2
3ui <- fluidPage(
4 actionButton("start_list", "Start 'list' iterator"),
5 actionButton("start_list2", "Start reactive 'list' iterator"),
6 actionButton("start_while", "Start 'while' iterator"),
7 actionButton("cancel", "Stop")
8)
9
10server <- function(input, output, session) {
11
12 val <- reactiveValues(b = 100)
13
14
15 base_task_iterator <- function(should_continue, iter_body) {
16 if (should_continue()) {
17 iter_body()
18 later::later(~base_task_iterator(should_continue, iter_body))
19 }
20 invisible()
21 }
22
23 while_task_iterator <- function(cancelExpr, whileExpr, func) {
24 cancelFunc <- rlang::as_function(rlang::enquo(cancelExpr))
25 whileFunc <- rlang::as_function(rlang::enquo(whileExpr))
26
27 origCancelVal <- isolate(try(silent = TRUE, cancelFunc()))
28 cancelled <- function() {
29 !identical(origCancelVal, isolate(try(silent = TRUE, cancelFunc())))
30 }
31
32 base_task_iterator(
33 function() {
34 !cancelled() && whileFunc()
35 },
36 func
37 )
38 }
39
40 list_task_iterator <- function(cancelExpr, x, func) {
41 cancelExpr <- rlang::enquo(cancelExpr)
42
43 origCancelVal <- isolate(try(silent = TRUE, cancelFunc()))
44 pos <- 1
45
46 while_task_iterator(!!cancelExpr, pos <= length(x), function() {
47 i <- pos
48 pos <<- pos + 1L
49 isolate({
50 func(x[[i]])
51 })
52 })
53 }
54
55 observeEvent(input$start_list, {
56 list_task_iterator(input$cancel, 1:10, function(x) {
57 message(x)
58 Sys.sleep(1)
59 })
60 })
61
62 observeEvent(input$start_list2, {
63 list_task_iterator(input$cancel, 1:10, function(x) {
64 val$b <- val$b + 1
65 message(val$b)
66 Sys.sleep(1)
67 })
68 })
69
70 observeEvent(input$start_while, {
71 # Something's wrong with rlang::as_function, I can't use TRUE, only !FALSE
72 while_task_iterator(input$cancel, !FALSE, function() {
73 message(format(Sys.time()))
74 Sys.sleep(1)
75 })
76 })
77}
78
79shinyApp(ui, server)