This is a helper function that behaves like then, however if
is.promising() returns FALSE then the handlers will be executed
immediately.
hybrid_then(expr, on_success = NULL, on_failure = NULL, ..., tee = FALSE)An expression that evaluates to either a promise or a non-promise value.
A function to be called when no error occurs synchronously
or asynchronously. 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. Can return a value or a promise.
A function to be called if an error occurs synchronously or
asynchronously. Takes one argument: the error object. Can return a value or
a promise to recover from the error, or throw a new error. If on_failure
is provided and doesn't throw an error (or return a promise that fails)
then this is the async equivalent of catching an error.
Reserved for future use. Currently must be empty.
If TRUE, ignore the return value of the callback, and use the
original value of expr as the result. For on_failure with tee = TRUE,
the callback executes but the original error is re-thrown afterward.
If expr evaluates to a promise, a promise with a single followup
promise to handle the on_success or on_failure callbacks.
If expr evaluates to a non-promise value, the result of the synchronous
operation after being processed by on_success or on_failure.
If a callback returns a promise, the result is always a promise.
Execution paths:
If expr evaluates to a promise (p), it will call p |> then(on_success, on_failure).
If expr evaluates to a non-promise value (x), it will call
on_success(x).
If expr throws an error (e) during calculation, it will call
on_failure(e).
In all cases, the on_success and on_failure callbacks are executed (when
provided).
This function is useful for writing functions that need to execute followup
behavior now or within a promise. This is different behavior than then()
where everything is made into a promise.
hybrid_then() allows authors to keep synchronous execution on the same
tick without requiring the use of a followup promise. This is particularly
appealing for situations where the author does not control the execution flow
for items that may be either synchronous or asynchronous, such as within
{plumber2}.
If no on_failure callback is provided and an error occurs, the error is
re-thrown immediately (for synchronous errors) or propagated through the
returned promise (for asynchronous errors).
If an on_failure callback is provided but it throws an error, that new
error replaces the original error. With tee = TRUE, even if on_failure
executes successfully, the original error is still re-thrown.
Callbacks can return any value, including promises. If a callback returns a
promise, the entire hybrid_then() call will return a promise, even if the
input was synchronous. This allows seamless transitions between synchronous
and asynchronous execution.
# Basic usage - works with both sync and async values
add_to <- function(x, k) {
hybrid_then(
x,
on_success = function(value) {
value + k
},
on_failure = function(err) {
message("Error: ", err$message)
NA_real_
}
)
}
# Synchronous
42 |> add_to(100)
#> [1] 142
#> [1] 142
# Synchronous error
add_to({stop("Bad input!")}, 8)
#> Error: Bad input!
#> [1] NA
#> Error: Bad input!
#> [1] NA
if (FALSE) { # \dontrun{
# Asynchronous
promise_resolve(42) |>
add_to(8) |>
then(print)
# When resolved...
#> [1] 50
# Error handling - asynchronous
promise_resolve(stop("Bad async input!")) |>
add_to(8) |>
then(print)
# When resolved...
#> Error: Bad async input!
#> [1] NA
# Chaining multiple operations
# (Move the `promise_resolve()` around to see sync vs async behavior)
1 |>
hybrid_then(on_success = \(x) x + 1) |>
hybrid_then(on_success = \(x) promise_resolve(x * 2)) |>
hybrid_then(on_success = \(x) x - 1) |>
hybrid_then(print)
# When resolved...
#> [1] 3
} # }