Nothing
#' Access the results of a promise
#'
#' Use the `then` function to access the eventual result of a promise (or, if the operation fails, the reason for that failure). Regardless of the state of the promise, the call to `then` is non-blocking, that is, it returns immediately; so what it does *not* do is immediately return the result value of the promise. Instead, you pass logic you want to execute to `then`, in the form of function callbacks (or formulas, see Details). If you provide an `onFulfilled` callback, it will be called upon the promise's successful resolution, with a single argument `value`: the result value. If you provide an `onRejected` callback, it will be called if the operation fails, with a single argument `reason`: the error that caused the failure.
#'
#' @section Formulas:
#'
#' For convenience, the `then()`, `catch()`, and `finally()` functions use
#' [rlang::as_function()] to convert `onFulfilled`, `onRejected`, and
#' `onFinally` arguments to functions. This means that you can use formulas to
#' create very compact anonymous functions, using `.` to access the value (in
#' the case of `onFulfilled`) or error (in the case of `onRejected`).
#'
#' @section Chaining promises:
#'
#' The first parameter of `then` is a promise; given the stated purpose of the
#' function, this should be no surprise. However, what may be surprising is that
#' the return value of `then` is also a (newly created) promise. This new
#' promise waits for the original promise to be fulfilled or rejected, and for
#' `onFulfilled` or `onRejected` to be called. The result of (or error raised
#' by) calling `onFulfilled`/`onRejected` will be used to fulfill (reject) the
#' new promise.
#'
#' ```
#' promise_a <- get_data_frame_async()
#' promise_b <- then(promise_a, onFulfilled = head)
#' ```
#'
#' In this example, assuming `get_data_frame_async` returns a promise that
#' eventually resolves to a data frame, `promise_b` will eventually resolve to
#' the first 10 or fewer rows of that data frame.
#'
#' Note that the new promise is considered fulfilled or rejected based on
#' whether `onFulfilled`/`onRejected` returns a value or throws an error, not on
#' whether the original promise was fulfilled or rejected. In other words, it's
#' possible to turn failure to success and success to failure. Consider this
#' example, where we expect `some_async_operation` to fail, and want to consider
#' it an error if it doesn't:
#'
#' ```
#' promise_c <- some_async_operation()
#' promise_d <- then(promise_c,
#' onFulfilled = function(value) {
#' stop("That's strange, the operation didn't fail!")
#' },
#' onRejected = function(reason) {
#' # Great, the operation failed as expected
#' NULL
#' }
#' )
#' ```
#'
#' Now, `promise_d` will be rejected if `promise_c` is fulfilled, and vice
#' versa.
#'
#' **Warning:** Be very careful not to accidentally turn failure into success,
#' if your error handling code is not the last item in a chain!
#'
#' ```
#' some_async_operation() %>%
#' catch(function(reason) {
#' warning("An error occurred: ", reason)
#' }) %>%
#' then(function() {
#' message("I guess we succeeded...?") # No!
#' })
#' ```
#'
#' In this example, the `catch` callback does not itself throw an error, so the
#' subsequent `then` call will consider its promise fulfilled!
#'
#' @section Convenience functions:
#'
#' For readability and convenience, we provide `catch` and `finally` functions.
#'
#' The `catch` function is equivalent to `then`, but without the `onFulfilled`
#' argument. It is typically used at the end of a promise chain to perform error
#' handling/logging.
#'
#' The `finally` function is similar to `then`, but takes a single no-argument
#' function (or formula) that will be executed upon completion of the promise,
#' regardless of whether the result is success or failure. It is typically used
#' at the end of a promise chain to perform cleanup tasks, like closing file
#' handles or database connections. Unlike `then` and `catch`, the return value
#' of `finally` is ignored; however, if an error is thrown in `finally`, that
#' error will be propagated forward into the returned promise.
#'
#' @section Visibility:
#'
#' `onFulfilled` functions can optionally have a second parameter `visible`,
#' which will be `FALSE` if the result value is [invisible][base::invisible()].
#'
#' @param promise A promise object. The object can be in any state.
#'
#' @param onFulfilled A function (or a formula--see Details) that will be
#' invoked if the promise value successfully resolves. When invoked, the
#' function will be called with a single argument: the resolved value.
#' Optionally, the function can take a second parameter `.visible` if you care
#' whether the promise was resolved with a visible or invisible value. The
#' function can return a value or a promise object, or can throw an error;
#' these will affect the resolution of the promise object that is returned
#' by `then()`.
#'
#' @param onRejected A function taking the argument `error` (or a formula--see
#' Details). The function can return a value or a promise object, or can throw
#' an error. If `onRejected` is provided and doesn't throw an error (or return
#' a promise that fails) then this is the async equivalent of catching an
#' error.
#'
#' @export
then <- function(promise, onFulfilled = NULL, onRejected = NULL) {
promise <- as.promise(promise)
if (!is.null(onFulfilled))
onFulfilled <- rlang::as_function(onFulfilled)
if (!is.null(onRejected))
onRejected <- rlang::as_function(onRejected)
invisible(promise$then(onFulfilled = onFulfilled, onRejected = onRejected))
}
#' @param tee If `TRUE`, ignore the return value of the callback, and use the
#' original value instead. This is useful for performing operations with
#' side-effects, particularly logging to the console or a file. If the
#' callback itself throws an error, and `tee` is `TRUE`, that error will still
#' be used to fulfill the the returned promise (in other words, `tee` only has
#' an effect if the callback does not throw).
#' @rdname then
#' @export
catch <- function(promise, onRejected, tee = FALSE) {
promise <- as.promise(promise)
if (!is.null(onRejected))
onRejected <- rlang::as_function(onRejected)
if (!tee) {
return(promise$catch(onRejected))
} else {
promise$catch(function(reason) {
onRejected(reason)
stop(reason)
})
}
}
#' @rdname then
#'
#' @param onFinally A function with no arguments, to be called when the async
#' operation either succeeds or fails. Usually used for freeing resources that
#' were used during async operations.
#'
#' @export
finally <- function(promise, onFinally) {
promise <- as.promise(promise)
if (!is.null(onFinally))
onFinally <- rlang::as_function(onFinally)
promise$finally(onFinally)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.