From 7c054f703341952913725ca04354a54a5c6ba1c1 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Tue, 27 Jan 2026 07:34:51 -0500 Subject: [PATCH] Signal progress via progressr --- DESCRIPTION | 2 +- R/future-map.R | 38 ++++++++++---- R/globals.R | 34 ++++++++++--- R/progress.R | 119 ------------------------------------------- R/template.R | 85 ++++++------------------------- R/utils.R | 8 +++ man/future_imap.Rd | 37 ++++++++++---- man/future_map.Rd | 37 ++++++++++---- man/future_map2.Rd | 37 ++++++++++---- man/future_map_if.Rd | 37 ++++++++++---- man/future_modify.Rd | 37 ++++++++++---- 11 files changed, 220 insertions(+), 251 deletions(-) delete mode 100644 R/progress.R diff --git a/DESCRIPTION b/DESCRIPTION index 5ae81c4..5d1dc93 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Depends: R (>= 4.1.0) Imports: globals (>= 0.18.0), + progressr (>= 0.18.0), purrr (>= 1.2.1), rlang (>= 1.1.7), vctrs (>= 0.7.0) @@ -31,7 +32,6 @@ Suggests: parallelly (>= 1.46.1), testthat (>= 3.3.2), tidyselect -Config/Needs/website:progressr Config/testthat/edition: 3 Encoding: UTF-8 Roxygen: list(markdown = TRUE) diff --git a/R/future-map.R b/R/future-map.R index 248dfa7..1e802db 100644 --- a/R/future-map.R +++ b/R/future-map.R @@ -45,15 +45,35 @@ #' @param .options The `future` specific options to use with the workers. This #' must be the result from a call to [furrr_options()]. #' -#' @param .progress A single logical. Should a progress bar be displayed? -#' Only works with multisession, multicore, and multiprocess futures. Note -#' that if a multicore/multisession future falls back to sequential, then -#' a progress bar will not be displayed. -#' -#' __Warning:__ The `.progress` argument will be deprecated and removed -#' in a future version of furrr in favor of using the more robust -#' [progressr](https://CRAN.R-project.org/package=progressr) -#' package. +#' @param .progress `r lifecycle::badge("experimental")` +#' +#' Either `TRUE` or `FALSE`. +#' +#' TODO!(write this): See `vignette("progress")` for examples. +#' +#' If `TRUE`, a `progressr::progressor()` is automatically created +#' with as many steps as there are elements to iterate over. After each +#' call to `.f`, progression is _signaled_ back to the main process. +#' +#' Note that `.progress = TRUE` is not enough to make progress notifications +#' _appear_. You must also either wrap the furrr expression in +#' [progressr::with_progress()] or set [`progressr::handlers(global = +#' TRUE)`][progressr::handlers()] at the top of your script (this enables +#' _all_ progress notifications, which is recommended for interative work). +#' +#' To customize the progress notification, set an alternative progressr +#' handler, such as +#' [`progressr::handlers(progressr::handler_cli())`][progressr::handler_cli()] +#' or +#' [`progressr::handlers(progressr::handler_rstudio())`][progressr::handler_rstudio()]. +#' +#' Note that progress notifications are not free, as they require +#' communicating a small amount of data back to the main worker after each +#' call to `.f`. It is recommended to only use them with long running tasks. +#' +#' All core `plan()` types are supported, including `sequential`, `cluster`, +#' `multisession`, and `multicore`. Additionally, `future.callr::callr` and +#' `future.mirai::mirai_multisession` are also known to be supported. #' #' @param .ptype If `NULL`, the default, the output type is the common type of #' the elements of the result. Otherwise, supply a "prototype" giving the diff --git a/R/globals.R b/R/globals.R index cb408e5..3bed025 100644 --- a/R/globals.R +++ b/R/globals.R @@ -3,24 +3,49 @@ get_globals_and_packages <- function( packages, fn, dots, + progressor, env_globals ) { objectSize <- import_future("objectSize") + # Needs to be `FutureGlobals` for `future:::c.FutureGlobals` to kick in + globals_out <- list() + globals_out <- future::as.FutureGlobals(globals_out) + globals_out <- future::resolve(globals_out) + globals_out <- set_total_size(globals_out, objectSize(globals_out)) + + packages_out <- character() + # Always need purrr on the worker - packages_out <- "purrr" + packages_out <- c(packages_out, "purrr") # Always get `.f` globals_fn <- list(...furrr_fn = fn) globals_fn <- future::as.FutureGlobals(globals_fn) globals_fn <- future::resolve(globals_fn) globals_fn <- set_total_size(globals_fn, objectSize(globals_fn)) + globals_out <- c(globals_out, globals_fn) # Always get `...`. globals_dots <- list(...furrr_dots = dots) globals_dots <- future::as.FutureGlobals(globals_dots) globals_dots <- future::resolve(globals_dots) globals_dots <- set_total_size(globals_dots, objectSize(globals_dots)) + globals_out <- c(globals_out, globals_dots) + + # Always get `progressor` if supplied + if (!is.null(progressor)) { + globals_progressor <- list(...furrr_progressor = progressor) + globals_progressor <- future::as.FutureGlobals(globals_progressor) + globals_progressor <- future::resolve(globals_progressor) + globals_progressor <- set_total_size( + globals_progressor, + objectSize(globals_progressor) + ) + globals_out <- c(globals_out, globals_progressor) + + packages_out <- c(packages_out, "progressr") + } # Always get chunk specific placeholders globals_extra <- list( @@ -31,12 +56,7 @@ get_globals_and_packages <- function( globals_extra <- future::as.FutureGlobals(globals_extra) globals_extra <- future::resolve(globals_extra) globals_extra <- set_total_size(globals_extra, objectSize(globals_extra)) - - globals_out <- c( - globals_fn, - globals_dots, - globals_extra - ) + globals_out <- c(globals_out, globals_extra) # Collect all globals recursively # Search in the parent frame of the `future_*()` call for globals diff --git a/R/progress.R b/R/progress.R deleted file mode 100644 index b8cf47d..0000000 --- a/R/progress.R +++ /dev/null @@ -1,119 +0,0 @@ -# nocov start - -poll_progress <- function(futures, file, n_x) { - symbol <- get_progress_symbol() - - prefix <- "Progress: " - suffix <- " 100%" - - width_prefix <- nchar(prefix) - width_suffix <- nchar(suffix) - width_carriage <- 1L - - stderr <- stderr() - - while (any_running(futures)) { - con <- file(file, open = "r") - n_ticks <- get_n_ticks(con) - close(con) - - # Console width might change while we poll - width_max <- console_width() - width_usable <- width_max - width_prefix - width_suffix - width_carriage - - # Supposedly someone has seen the multiplication integer overflow for - # extremely long inputs, so we `as.double()` one of the operands, but the - # result after division should still always fit in an integer (#288) - width_rule <- as.integer(floor(width_usable * as.double(n_ticks) / n_x)) - width_space <- width_usable - width_rule - - space <- paste0(rep(" ", times = width_space), collapse = "") - rule <- paste0(rep(symbol, times = width_rule), collapse = "") - - out <- paste0(prefix, rule, space, suffix) - - cat("\r", out, file = stderr) - utils::flush.console() - } -} - -get_n_ticks <- function(con) { - line <- readLines(con, n = 1L, warn = FALSE) - - if (length(line) == 0L) { - line <- "" - } - - nchar(line) -} - -any_running <- function(futures) { - !all(future::resolved(futures)) -} - -console_width <- function() { - width <- Sys.getenv("RSTUDIO_CONSOLE_WIDTH", getOption("width", 80)) - as.integer(width) -} - -# ------------------------------------------------------------------------------ - -# Adapted from cli's onload properties -# to dynamically switch depending on utf8 availability -get_progress_symbol <- function() { - if (is_utf8_output()) { - "\u2500" - } else { - "-" - } -} - -is_utf8_output <- function() { - l10n_info()$`UTF-8` && !is_latex_output() -} - -is_latex_output <- function() { - if (!("knitr" %in% loadedNamespaces())) { - return(FALSE) - } - - get("is_latex_output", asNamespace("knitr"))() -} - -# ------------------------------------------------------------------------------ - -assert_progress <- function(progress) { - if (!is_bool(progress)) { - abort("`.progress` must be a single logical value.") - } - - invisible(progress) -} - -# ------------------------------------------------------------------------------ - -# - Sequential blocks in the `future()` call, so no progress is ever shown -# - Cluster is generally used for multi-computer setups, and would end up -# writing into files on the remote workers, which would never be shown. -progress_enabled_plans <- c( - "multicore", - "multisession", - "multiprocess" -) - -reconcile_progress_with_strategy <- function(progress) { - if (is_false(progress)) { - return(progress) - } - - plan <- future::plan() - progress_enabled_plan <- inherits_any(plan, progress_enabled_plans) - - if (!progress_enabled_plan) { - progress <- FALSE - } - - progress -} - -# nocov end diff --git a/R/template.R b/R/template.R index 19e28d9..ff4a51c 100644 --- a/R/template.R +++ b/R/template.R @@ -14,12 +14,9 @@ furrr_map_template <- function( assert_furrr_options(options) assert_progress(progress) - progress <- reconcile_progress_with_strategy(progress) - expr_seed_setup <- make_expr_seed_setup(options$seed) expr_seed_update <- make_expr_seed_update(options$seed) - expr_progress_setup <- make_expr_progress_setup(progress) expr_progress_update <- make_expr_progress_update(progress) walk <- identical(purrr_fn_name, "walk") @@ -34,14 +31,14 @@ furrr_map_template <- function( ...furrr_chunk_x <- ...furrr_chunk_args !!expr_seed_setup - !!expr_progress_setup ...furrr_fn_wrapper <- function(...) { !!expr_seed_update - !!expr_progress_update ...furrr_out <- ...furrr_fn(...) + !!expr_progress_update + !!expr_out } @@ -105,12 +102,9 @@ furrr_map2_template <- function( assert_furrr_options(options) assert_progress(progress) - progress <- reconcile_progress_with_strategy(progress) - expr_seed_setup <- make_expr_seed_setup(options$seed) expr_seed_update <- make_expr_seed_update(options$seed) - expr_progress_setup <- make_expr_progress_setup(progress) expr_progress_update <- make_expr_progress_update(progress) walk <- identical(purrr_fn_name, "walk2") @@ -126,14 +120,14 @@ furrr_map2_template <- function( ...furrr_chunk_y <- ...furrr_chunk_args[[2]] !!expr_seed_setup - !!expr_progress_setup ...furrr_fn_wrapper <- function(...) { !!expr_seed_update - !!expr_progress_update ...furrr_out <- ...furrr_fn(...) + !!expr_progress_update + !!expr_out } @@ -204,12 +198,9 @@ furrr_pmap_template <- function( assert_furrr_options(options) assert_progress(progress) - progress <- reconcile_progress_with_strategy(progress) - expr_seed_setup <- make_expr_seed_setup(options$seed) expr_seed_update <- make_expr_seed_update(options$seed) - expr_progress_setup <- make_expr_progress_setup(progress) expr_progress_update <- make_expr_progress_update(progress) walk <- identical(purrr_fn_name, "pwalk") @@ -224,14 +215,14 @@ furrr_pmap_template <- function( ...furrr_chunk_l <- ...furrr_chunk_args !!expr_seed_setup - !!expr_progress_setup ...furrr_fn_wrapper <- function(...) { !!expr_seed_update - !!expr_progress_update ...furrr_out <- ...furrr_fn(...) + !!expr_progress_update + !!expr_out } @@ -315,11 +306,18 @@ furrr_template <- function( chunks <- map(chunks, function(chunk) .subset(order, chunk)) } + if (progress) { + progressor <- progressr::progressor(steps = n) + } else { + progressor <- NULL + } + gp <- get_globals_and_packages( options$globals, options$packages, fn, dots, + progressor, env_globals ) @@ -354,24 +352,6 @@ furrr_template <- function( labels <- paste0(options$prefix, "-", seq_len(n_chunks)) } - if (progress) { - # nocov start - objectSize <- import_future("objectSize") - - file <- tempfile(fileext = ".txt") - - file.create(file) - on.exit(unlink(file, force = TRUE), add = TRUE) - - globals_file <- list(...furrr_progress_file = file) - globals_file <- future::as.FutureGlobals(globals_file) - globals_file <- future::resolve(globals_file) - globals_file <- set_total_size(globals_file, objectSize(globals_file)) - - globals <- c(globals, globals_file) - # nocov end - } - scan_chunk_args_for_globals <- is_true(options$globals) futures <- vector("list", length = n_chunks) @@ -434,10 +414,6 @@ furrr_template <- function( ) } - if (progress) { - poll_progress(futures, file, n) - } - values <- future::value(futures) if (length(values) != length(chunks)) { @@ -493,41 +469,12 @@ make_expr_seed_update <- function(seed) { # nocov start -make_expr_progress_setup <- function(progress) { - if (is_false(progress)) { - return(NULL) - } - - expr({ - ...furrr_progress <- TRUE - - tryCatch( - expr = { - ...furrr_progress_con <- file(...furrr_progress_file, open = "a") - on.exit(close(...furrr_progress_con), add = TRUE) - }, - error = function(cnd) { - ...furrr_progress <<- FALSE - } - ) - }) -} - make_expr_progress_update <- function(progress) { if (is_false(progress)) { return(NULL) } - expr({ - if (...furrr_progress) { - try( - expr = { - cat("+", file = ...furrr_progress_con, sep = "") - }, - silent = TRUE - ) - } - }) + expr(...furrr_progressor()) } # nocov end @@ -565,8 +512,6 @@ utils::globalVariables( "...furrr_globals_max_size", "...furrr_chunk_seeds", "...furrr_chunk_seeds_env", - "...furrr_progress", - "...furrr_progress_file", - "...furrr_progress_con" + "...furrr_progressor" ) ) diff --git a/R/utils.R b/R/utils.R index 4fe8745..14fad53 100644 --- a/R/utils.R +++ b/R/utils.R @@ -24,3 +24,11 @@ set_total_size <- function(x, total_size) { attr(x, "total_size") <- total_size x } + +assert_progress <- function(progress) { + if (!is_bool(progress)) { + abort("`.progress` must be a single logical value.") + } + + invisible(progress) +} diff --git a/man/future_imap.Rd b/man/future_imap.Rd index 4bc8719..41c9cac 100644 --- a/man/future_imap.Rd +++ b/man/future_imap.Rd @@ -128,15 +128,34 @@ must be the result from a call to \code{\link[=furrr_options]{furrr_options()}}. \code{...}. Globals required by \code{.f} are looked up in the function environment of \code{.f}.} -\item{.progress}{A single logical. Should a progress bar be displayed? -Only works with multisession, multicore, and multiprocess futures. Note -that if a multicore/multisession future falls back to sequential, then -a progress bar will not be displayed. - -\strong{Warning:} The \code{.progress} argument will be deprecated and removed -in a future version of furrr in favor of using the more robust -\href{https://CRAN.R-project.org/package=progressr}{progressr} -package.} +\item{.progress}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} + +Either \code{TRUE} or \code{FALSE}. + +TODO!(write this): See \code{vignette("progress")} for examples. + +If \code{TRUE}, a \code{progressr::progressor()} is automatically created +with as many steps as there are elements to iterate over. After each +call to \code{.f}, progression is \emph{signaled} back to the main process. + +Note that \code{.progress = TRUE} is not enough to make progress notifications +\emph{appear}. You must also either wrap the furrr expression in +\code{\link[progressr:with_progress]{progressr::with_progress()}} or set \code{\link[progressr:handlers]{progressr::handlers(global = TRUE)}} at the top of your script (this enables +\emph{all} progress notifications, which is recommended for interative work). + +To customize the progress notification, set an alternative progressr +handler, such as +\code{\link[progressr:handler_cli]{progressr::handlers(progressr::handler_cli())}} +or +\code{\link[progressr:handler_rstudio]{progressr::handlers(progressr::handler_rstudio())}}. + +Note that progress notifications are not free, as they require +communicating a small amount of data back to the main worker after each +call to \code{.f}. It is recommended to only use them with long running tasks. + +All core \code{plan()} types are supported, including \code{sequential}, \code{cluster}, +\code{multisession}, and \code{multicore}. Additionally, \code{future.callr::callr} and +\code{future.mirai::mirai_multisession} are also known to be supported.} \item{.ptype}{If \code{NULL}, the default, the output type is the common type of the elements of the result. Otherwise, supply a "prototype" giving the diff --git a/man/future_map.Rd b/man/future_map.Rd index b4d8d8e..cfac6b2 100644 --- a/man/future_map.Rd +++ b/man/future_map.Rd @@ -131,15 +131,34 @@ must be the result from a call to \code{\link[=furrr_options]{furrr_options()}}. \code{...}. Globals required by \code{.f} are looked up in the function environment of \code{.f}.} -\item{.progress}{A single logical. Should a progress bar be displayed? -Only works with multisession, multicore, and multiprocess futures. Note -that if a multicore/multisession future falls back to sequential, then -a progress bar will not be displayed. - -\strong{Warning:} The \code{.progress} argument will be deprecated and removed -in a future version of furrr in favor of using the more robust -\href{https://CRAN.R-project.org/package=progressr}{progressr} -package.} +\item{.progress}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} + +Either \code{TRUE} or \code{FALSE}. + +TODO!(write this): See \code{vignette("progress")} for examples. + +If \code{TRUE}, a \code{progressr::progressor()} is automatically created +with as many steps as there are elements to iterate over. After each +call to \code{.f}, progression is \emph{signaled} back to the main process. + +Note that \code{.progress = TRUE} is not enough to make progress notifications +\emph{appear}. You must also either wrap the furrr expression in +\code{\link[progressr:with_progress]{progressr::with_progress()}} or set \code{\link[progressr:handlers]{progressr::handlers(global = TRUE)}} at the top of your script (this enables +\emph{all} progress notifications, which is recommended for interative work). + +To customize the progress notification, set an alternative progressr +handler, such as +\code{\link[progressr:handler_cli]{progressr::handlers(progressr::handler_cli())}} +or +\code{\link[progressr:handler_rstudio]{progressr::handlers(progressr::handler_rstudio())}}. + +Note that progress notifications are not free, as they require +communicating a small amount of data back to the main worker after each +call to \code{.f}. It is recommended to only use them with long running tasks. + +All core \code{plan()} types are supported, including \code{sequential}, \code{cluster}, +\code{multisession}, and \code{multicore}. Additionally, \code{future.callr::callr} and +\code{future.mirai::mirai_multisession} are also known to be supported.} \item{.ptype}{If \code{NULL}, the default, the output type is the common type of the elements of the result. Otherwise, supply a "prototype" giving the diff --git a/man/future_map2.Rd b/man/future_map2.Rd index d16ec9f..f8c3730 100644 --- a/man/future_map2.Rd +++ b/man/future_map2.Rd @@ -230,15 +230,34 @@ must be the result from a call to \code{\link[=furrr_options]{furrr_options()}}. \code{...}. Globals required by \code{.f} are looked up in the function environment of \code{.f}.} -\item{.progress}{A single logical. Should a progress bar be displayed? -Only works with multisession, multicore, and multiprocess futures. Note -that if a multicore/multisession future falls back to sequential, then -a progress bar will not be displayed. - -\strong{Warning:} The \code{.progress} argument will be deprecated and removed -in a future version of furrr in favor of using the more robust -\href{https://CRAN.R-project.org/package=progressr}{progressr} -package.} +\item{.progress}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} + +Either \code{TRUE} or \code{FALSE}. + +TODO!(write this): See \code{vignette("progress")} for examples. + +If \code{TRUE}, a \code{progressr::progressor()} is automatically created +with as many steps as there are elements to iterate over. After each +call to \code{.f}, progression is \emph{signaled} back to the main process. + +Note that \code{.progress = TRUE} is not enough to make progress notifications +\emph{appear}. You must also either wrap the furrr expression in +\code{\link[progressr:with_progress]{progressr::with_progress()}} or set \code{\link[progressr:handlers]{progressr::handlers(global = TRUE)}} at the top of your script (this enables +\emph{all} progress notifications, which is recommended for interative work). + +To customize the progress notification, set an alternative progressr +handler, such as +\code{\link[progressr:handler_cli]{progressr::handlers(progressr::handler_cli())}} +or +\code{\link[progressr:handler_rstudio]{progressr::handlers(progressr::handler_rstudio())}}. + +Note that progress notifications are not free, as they require +communicating a small amount of data back to the main worker after each +call to \code{.f}. It is recommended to only use them with long running tasks. + +All core \code{plan()} types are supported, including \code{sequential}, \code{cluster}, +\code{multisession}, and \code{multicore}. Additionally, \code{future.callr::callr} and +\code{future.mirai::mirai_multisession} are also known to be supported.} \item{.ptype}{If \code{NULL}, the default, the output type is the common type of the elements of the result. Otherwise, supply a "prototype" giving the diff --git a/man/future_map_if.Rd b/man/future_map_if.Rd index 21f3d7d..272a284 100644 --- a/man/future_map_if.Rd +++ b/man/future_map_if.Rd @@ -72,15 +72,34 @@ must be the result from a call to \code{\link[=furrr_options]{furrr_options()}}. \code{...}. Globals required by \code{.f} are looked up in the function environment of \code{.f}.} -\item{.progress}{A single logical. Should a progress bar be displayed? -Only works with multisession, multicore, and multiprocess futures. Note -that if a multicore/multisession future falls back to sequential, then -a progress bar will not be displayed. - -\strong{Warning:} The \code{.progress} argument will be deprecated and removed -in a future version of furrr in favor of using the more robust -\href{https://CRAN.R-project.org/package=progressr}{progressr} -package.} +\item{.progress}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} + +Either \code{TRUE} or \code{FALSE}. + +TODO!(write this): See \code{vignette("progress")} for examples. + +If \code{TRUE}, a \code{progressr::progressor()} is automatically created +with as many steps as there are elements to iterate over. After each +call to \code{.f}, progression is \emph{signaled} back to the main process. + +Note that \code{.progress = TRUE} is not enough to make progress notifications +\emph{appear}. You must also either wrap the furrr expression in +\code{\link[progressr:with_progress]{progressr::with_progress()}} or set \code{\link[progressr:handlers]{progressr::handlers(global = TRUE)}} at the top of your script (this enables +\emph{all} progress notifications, which is recommended for interative work). + +To customize the progress notification, set an alternative progressr +handler, such as +\code{\link[progressr:handler_cli]{progressr::handlers(progressr::handler_cli())}} +or +\code{\link[progressr:handler_rstudio]{progressr::handlers(progressr::handler_rstudio())}}. + +Note that progress notifications are not free, as they require +communicating a small amount of data back to the main worker after each +call to \code{.f}. It is recommended to only use them with long running tasks. + +All core \code{plan()} types are supported, including \code{sequential}, \code{cluster}, +\code{multisession}, and \code{multicore}. Additionally, \code{future.callr::callr} and +\code{future.mirai::mirai_multisession} are also known to be supported.} \item{.at}{A logical, integer, or character vector giving the elements to select. Alternatively, a function that takes a vector of names, diff --git a/man/future_modify.Rd b/man/future_modify.Rd index cfe7a76..1a4d5bd 100644 --- a/man/future_modify.Rd +++ b/man/future_modify.Rd @@ -63,15 +63,34 @@ must be the result from a call to \code{\link[=furrr_options]{furrr_options()}}. \code{...}. Globals required by \code{.f} are looked up in the function environment of \code{.f}.} -\item{.progress}{A single logical. Should a progress bar be displayed? -Only works with multisession, multicore, and multiprocess futures. Note -that if a multicore/multisession future falls back to sequential, then -a progress bar will not be displayed. - -\strong{Warning:} The \code{.progress} argument will be deprecated and removed -in a future version of furrr in favor of using the more robust -\href{https://CRAN.R-project.org/package=progressr}{progressr} -package.} +\item{.progress}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} + +Either \code{TRUE} or \code{FALSE}. + +TODO!(write this): See \code{vignette("progress")} for examples. + +If \code{TRUE}, a \code{progressr::progressor()} is automatically created +with as many steps as there are elements to iterate over. After each +call to \code{.f}, progression is \emph{signaled} back to the main process. + +Note that \code{.progress = TRUE} is not enough to make progress notifications +\emph{appear}. You must also either wrap the furrr expression in +\code{\link[progressr:with_progress]{progressr::with_progress()}} or set \code{\link[progressr:handlers]{progressr::handlers(global = TRUE)}} at the top of your script (this enables +\emph{all} progress notifications, which is recommended for interative work). + +To customize the progress notification, set an alternative progressr +handler, such as +\code{\link[progressr:handler_cli]{progressr::handlers(progressr::handler_cli())}} +or +\code{\link[progressr:handler_rstudio]{progressr::handlers(progressr::handler_rstudio())}}. + +Note that progress notifications are not free, as they require +communicating a small amount of data back to the main worker after each +call to \code{.f}. It is recommended to only use them with long running tasks. + +All core \code{plan()} types are supported, including \code{sequential}, \code{cluster}, +\code{multisession}, and \code{multicore}. Additionally, \code{future.callr::callr} and +\code{future.mirai::mirai_multisession} are also known to be supported.} \item{.at}{A logical, integer, or character vector giving the elements to select. Alternatively, a function that takes a vector of names,