mirror of
https://github.com/rstudio/shiny.git
synced 2026-04-29 03:00:45 -04:00
fix: Wrap extended task invocation in promise_resolve() (#4225)
* fix: Wrap extended task invocation in `promise_resolve()` * refactor: cleanup error handling and promise chain * chore: add news entry
This commit is contained in:
committed by
GitHub
parent
6df0bb9423
commit
eac0eea886
4
NEWS.md
4
NEWS.md
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
* Shiny's Typescript assets are now compiled to ES2021 instead of ES5. (#4066)
|
* Shiny's Typescript assets are now compiled to ES2021 instead of ES5. (#4066)
|
||||||
|
|
||||||
|
* `ExtendedTask` now catches synchronous values and errors and returns them via `$result()`. Previously, the extended task function was required to always return a promise. This change makes it easier to use `ExtendedTask` with a function that may return early or do some synchronous work before returning a promise. (#4225)
|
||||||
|
|
||||||
## Bug fixes
|
## Bug fixes
|
||||||
|
|
||||||
* Fixed a bug with modals where calling `removeModal()` too quickly after `showModal()` would fail to remove the modal if the remove modal message was received while the modal was in the process of being revealed. (#4173)
|
* Fixed a bug with modals where calling `removeModal()` too quickly after `showModal()` would fail to remove the modal if the remove modal message was received while the modal was in the process of being revealed. (#4173)
|
||||||
@@ -31,6 +33,8 @@
|
|||||||
|
|
||||||
* Updated the JavaScript used when inserting a tab to avoid rendering dynamic UI elements twice when adding the new tab via `insertTab()` or `bslib::nav_insert()`. (#4179)
|
* Updated the JavaScript used when inserting a tab to avoid rendering dynamic UI elements twice when adding the new tab via `insertTab()` or `bslib::nav_insert()`. (#4179)
|
||||||
|
|
||||||
|
* Fixed an issue with `ExtendedTask` where synchronous errors would cause an error that would stop the current session. (#4225)
|
||||||
|
|
||||||
# shiny 1.10.0
|
# shiny 1.10.0
|
||||||
|
|
||||||
## New features and improvements
|
## New features and improvements
|
||||||
|
|||||||
@@ -130,14 +130,15 @@ ExtendedTask <- R6Class("ExtendedTask", portable = TRUE, cloneable = FALSE,
|
|||||||
#' arguments.
|
#' arguments.
|
||||||
invoke = function(...) {
|
invoke = function(...) {
|
||||||
args <- rlang::dots_list(..., .ignore_empty = "none")
|
args <- rlang::dots_list(..., .ignore_empty = "none")
|
||||||
|
call <- rlang::caller_call(n = 0)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isolate(private$rv_status()) == "running" ||
|
isolate(private$rv_status()) == "running" ||
|
||||||
private$invocation_queue$size() > 0
|
private$invocation_queue$size() > 0
|
||||||
) {
|
) {
|
||||||
private$invocation_queue$add(args)
|
private$invocation_queue$add(list(args = args, call = call))
|
||||||
} else {
|
} else {
|
||||||
private$do_invoke(args)
|
private$do_invoke(args, call = call)
|
||||||
}
|
}
|
||||||
invisible(NULL)
|
invisible(NULL)
|
||||||
},
|
},
|
||||||
@@ -204,44 +205,41 @@ ExtendedTask <- R6Class("ExtendedTask", portable = TRUE, cloneable = FALSE,
|
|||||||
rv_error = NULL,
|
rv_error = NULL,
|
||||||
invocation_queue = NULL,
|
invocation_queue = NULL,
|
||||||
|
|
||||||
do_invoke = function(args) {
|
do_invoke = function(args, call = NULL) {
|
||||||
private$rv_status("running")
|
private$rv_status("running")
|
||||||
private$rv_value(NULL)
|
private$rv_value(NULL)
|
||||||
private$rv_error(NULL)
|
private$rv_error(NULL)
|
||||||
|
|
||||||
p <- NULL
|
p <- promises::promise_resolve(
|
||||||
tryCatch({
|
maskReactiveContext(do.call(private$func, args))
|
||||||
maskReactiveContext({
|
)
|
||||||
# TODO: Bounce the do.call off of a promise_resolve(), so that the
|
|
||||||
# call to invoke() always returns immediately?
|
|
||||||
result <- do.call(private$func, args)
|
|
||||||
p <- promises::as.promise(result)
|
|
||||||
})
|
|
||||||
}, error = function(e) {
|
|
||||||
private$on_error(e)
|
|
||||||
})
|
|
||||||
|
|
||||||
promises::finally(
|
p <- promises::then(
|
||||||
promises::then(p,
|
p,
|
||||||
onFulfilled = function(value, .visible) {
|
onFulfilled = function(value, .visible) {
|
||||||
private$on_success(list(value=value, visible=.visible))
|
private$on_success(list(value = value, visible = .visible))
|
||||||
},
|
},
|
||||||
onRejected = function(error) {
|
onRejected = function(error) {
|
||||||
private$on_error(error)
|
private$on_error(error, call = call)
|
||||||
}
|
|
||||||
),
|
|
||||||
onFinally = function() {
|
|
||||||
if (private$invocation_queue$size() > 0) {
|
|
||||||
private$do_invoke(private$invocation_queue$remove())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
promises::finally(p, onFinally = function() {
|
||||||
|
if (private$invocation_queue$size() > 0) {
|
||||||
|
next_call <- private$invocation_queue$remove()
|
||||||
|
private$do_invoke(next_call$args, next_call$call)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
invisible(NULL)
|
invisible(NULL)
|
||||||
},
|
},
|
||||||
|
|
||||||
on_error = function(err) {
|
on_error = function(err, call = NULL) {
|
||||||
|
cli::cli_warn(
|
||||||
|
"ERROR: An error occurred when invoking the ExtendedTask.",
|
||||||
|
parent = err,
|
||||||
|
call = call
|
||||||
|
)
|
||||||
private$rv_status("error")
|
private$rv_status("error")
|
||||||
private$rv_error(err)
|
private$rv_error(err)
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user