rstudio / promises

A promise library for R
https://rstudio.github.io/promises
Other
201 stars 19 forks source link

Conditions signaled from promises are invisible to the invoking environment #70

Closed tzakharko closed 3 years ago

tzakharko commented 3 years ago

It seems that any signaled condition inside a promise is gobbled up by some hidden mechanism and will not be visible to the external handlers. The attached code illustrates this with messages (because they have a clear side effect), but I also tried it with other signals with the same effect.

Is there any obvious way to access signaled conditions outside of using external mechanisms (e.g. a callback).

library(promises)

withCallingHandlers({
    promise(~ {
     later::later(~ {
        message("this is a message")
        resolve(TRUE)
     })
    }) %>% finally(~ {
      message("we are done")
      cat("Promise is resovled\n")
    })

    while(!later::loop_empty()) {
      later::run_now()
      Sys.sleep(0.1)
      message("from the main loop")
    }
  },
  message = function(cnd) {
    cat("Got message:", cnd$message)
  }
)
#> Got message: from the main loop
#> from the main loop
#> Promise is resovled
#> Got message: from the main loop
#> from the main loop
#> Got message: from the main loop
#> from the main loop
tzakharko commented 3 years ago

After digging a bit into this I realized that this is a limitation of later, so I will ask this question there.

bartekch commented 1 year ago

@tzakharko what issue in later is this? Is it solved maybe?

gadenbuie commented 1 year ago

I didn't find any issues in later opened by @tzakharko or that match the topic discussed here.

It's hard to tell the full context of the original question, but if you're wanting to handle errors from code being evaluated as a promise, I recommend taking a look at the Access the results of a promise article in the promises docs. You can use the onRejected argument of then() or the catch() function to handle errors that are signaled in a promise chain.

@bartekch do feel free to open a new issue for your question, especially if you can create a reprex for it.

tzakharko commented 1 year ago

I have to admit that I don't remember the exact context. It doesn't seem I ever opened an issue with later, so I suppose I have abandoned this particular line of inquiry. At any rate, I have tried this snippet and it still does not handle the conditions from inside the promise.

gadenbuie commented 1 year ago

@tzakharko Thanks for checking back in! In terms of the snippet in this issue; it's definitely not expected to work. The conditions aren't signaled in a place visible to withCallingHandlers() in the parent process. To handle errors, you need to use catch() or the onRejected argument of then(). You might be able to catch other conditions in this way, too, or you might need to bring the condition handler into the block being evaluated in a promise.

jcheng5 commented 1 year ago

For (very) advanced scenarios, you can accomplish something that's sort of like withCallingHandlers for promises with promise domains: https://rstudio.github.io/promises/reference/with_promise_domain.html

Though, I would be slightly surprised if this is useful to anyone, unless they are writing a framework of some kind.

bartekch commented 1 year ago

@gadenbuie my case is almost exactly the same as OP - I want to intercept messages signaled in onFulfilled (or onRejected, same story). Simplest example.

withCallingHandlers(
  {
    message("This message will be caught")
    promises::future_promise(Sys.sleep(1)) |>
      promises::then(function() message("I want this meesage to be caught as well"))
  },
  message = function(cnd) {
    message("Got message: ", cnd$message)
  }
)
#> Got message: This message will be caught

#> This message will be caught
#> I want this meesage to be caught as well

I thought that it would work, since callback is run in the original process. I also got confused by the sentence However, it’s perfectly fine to read reactive values/expressions from inside a promise handler. Handlers run in the original process, not a child process, so reactive operations are allowed. in vignette about promises in Shiny (my actual case is intercepting all messages in Shiny app and appending common prefix, for logging purposes, but I guess it doesn't really matter).

From your comments I infer that it does NOT work like that. And, after some consideration, it actually makes some sense, because whole call to withCallingHandlers is completed before promise is resolved. It's just a little less obvious when running sequentially (or inside Shiny, as I did). Hence I won't open another issue for this.

@jcheng5 I'm not exactly sure how should I use domains to address my issue, but anyway it seems like way too complicated for what I need, thanks for suggestion though!

In the meantime I believe I found the forementioned issue in later - still open though.