rasynchronouspromisefuture

Get value from R fullfilled promise


I read many articles about R promises (including this) and still don't get it.

See code:

library(future)
library(promises)
plan(multiprocess)

read.csv.async <- function(file, header = TRUE, stringsAsFactors = FALSE) {
  future({
    read.csv(file, header = header, stringsAsFactors = stringsAsFactors)
  })
}

df_promise <- read.csv.async("https://rstudio.github.io/promises/data.csv")

df_promise %...>% filter(state == "NY")

df_filtered_promise <- df_promise %...>% filter(state == "NY")

df_filtered_promise

class(df_filtered_promise)

Output:

> read.csv.async <- function(file, header = TRUE, stringsAsFactors = FALSE) {
+   future({
+     read.csv(file, header = header, stringsAsFactors = stringsAsFactors)
+   })
+ }
> 
> df_promise <- read.csv.async("https://rstudio.github.io/promises/data.csv")
> 
> df_promise %...>% filter(state == "NY")
> 
> df_filtered_promise <- df_promise %...>% filter(state == "NY")
> 
> df_filtered_promise
<Promise [pending]>
> df_filtered_promise
<Promise [fulfilled: data.frame]>
> class(df_filtered_promise)
[1] "promise"

Why fullfilled promise doesn't return its value? How can I extract data frame in my case?


Solution

  • There is a way to do this, but before I tell you, I would advise you not to use promises for interactive or scripting use, probably. Synchronous programming is more convenient than asynchronous, and you should only do the latter if it's very important not to tie up the main R thread (as is the case with Shiny if you want to keep apps responsive while long operations run).

    If you choose to use future, try to get away with not chaining any %...>% operations after it, and then you can simply use future::value as Daniel Fischer said.

    If you do decide to fully use promises, and it's important to you to extract the value out into a regular variable, then you can accomplish this via side effects, like this super-assign:

    df_filtered <- NULL
    df_filtered_promise %...>% { df_filtered <<- . }
    

    This will cause the df_filtered variable to be set to the result of df_filtered_promise at some point in the future. (It will never be assigned in the case of error, though.)

    This generally ought not be done in Shiny, as you usually want to keep things wrapped in promises all the way to the end of the computation so Shiny can keep track of what outputs/observers are waiting on what operations.