Taz
Taz

Reputation: 5983

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?

Upvotes: 6

Views: 1794

Answers (4)

ypa y yhm
ypa y yhm

Reputation: 219

promise %>% 
  {.$then} %>% 
  environment %>% 
  {.$private$value}

You can try this.

If promise is not fullfilled, this will return NULL.


e.g.

seq(3) %>% promises::promise_map(\ (a) a+1) -> promise

(\ (promise) promise %>% 
    {.$then} %>% environment %>% 
    {while (.$private$state != "fulfilled") {
        
        message (.$private$state," ..."); Sys.sleep(1)
        }; .$private$value} %>% 
    
{.}) (promise)

If you submit these code to console once, it will prnding ... forever; but if you submit these code one by one, it will return the result. So if you want to get the result out of the promise box in a script, you will fail.

So how can we use the value inside that promise box ? The answer (may be only one) is use then or %...>% pipe. And if you have many value in several promise box, you can do something like this:

seq(3) %>% promises::promise_map(\ (a) a+1) -> promise0
seq(1) %>% promises::promise_map(\ (a) a+1) -> promise1

promise0 %>% 
    promises::then (\ (a) promise1 %>% 
        promises::then (\ (b) (a %>% unlist) - (b %>% unlist))) %>% 
    promises::then (\ (x) x %T>% print %>% class %>% print)

Or clearly, use promise_all:

seq(3) %>% promises::promise_map (\ (a) a+1) -> promise0
seq(1) %>% promises::promise_map (\ (a) a+1) -> promise1

promise_ab = promises::promise_all (a = promise0, b = promise1)

promise_ab %>% 
    promises::then (\ (x) (x$a %>% unlist) - (x$b %>% unlist)) %>% 
    promises::then (\ (x) x %T>% print %>% class %>% print)

(For me, I like the future more ... )

Upvotes: 1

UD22
UD22

Reputation: 51

df <- environment (promise_object[["then"]])[["private"]][["value"]] is what you are looking for.

Promise object stores data and properties in a nested list format and that list is part of the environment object.

Upvotes: 5

Joe Cheng
Joe Cheng

Reputation: 8061

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.

Upvotes: 9

Daniel Fischer
Daniel Fischer

Reputation: 973

you have to use value function:

value(df_filtered_promise)

Upvotes: -2

Related Questions