The progressr package works seamlessly with parallel and distributed processing using futureverse, and it will also provide near-live progress updates while the parallel processing is still running. For example,
library(progressr)
handlers("progress", global = TRUE)
library(futurize)
plan(multisession, workers = 2)
my_fcn <- function(xs) {
p <- progressr::progressor(along = xs)
lapply(xs, function(x, ...) {
Sys.sleep((10.0-x)/2)
p(sprintf("x=%g", x))
sqrt(x)
}) |> futurize()
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40% x=2
Alternatively, we can use progressify() from the progressify package
to automatically add progress reporting without modifying the function's
internal code:
library(progressify)
handlers("progress", global = TRUE)
library(futurize)
plan(multisession, workers = 2)
my_fcn <- function(xs) {
lapply(xs, function(x, ...) {
Sys.sleep((10.0-x)/2)
sqrt(x)
}) |> progressify() |> futurize()
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40%
The futureverse framework, which provides a unified API for parallel
and distributed processing in R, has built-in support for the kind of
progression updates produced by the progressr package. The modern,
recommended approach to parallelize such code is using futurize() from
the futurize package, which supports common map-reduce and iteration
functions like lapply(), purrr::map(), foreach(), bplapply(), and
llply().
Traditional parallelization packages such as future.apply, furrr,
and foreach with doFuture (specifically, %dofuture% or registered
via registerDoFuture()) are still fully supported and can be used as alternatives.
In contrast, non-future parallelization methods such as parallel's
mclapply() and parallel::parLapply(), and foreach adapters like
doParallel do not support progress reports via progressr.
Here is an example that uses futurize() of the futurize package to parallelize a standard lapply() call on the local machine while at the same time signaling progression updates:
library(progressr)
handlers("progress", global = TRUE)
library(futurize)
plan(multisession, workers = 2)
my_fcn <- function(xs) {
p <- progressor(along = xs)
lapply(xs, function(x, ...) {
Sys.sleep((10.0-x)/2)
p(sprintf("x=%g", x))
sqrt(x)
}) |> futurize()
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40% x=2
Note that using future_lapply() of the future.apply package is still supported as a traditional alternative.
Here is an example that uses foreach() of the foreach package
together with futurize() of the futurize package to
parallelize a standard sequential %do% loop while reporting on progress.
This example parallelizes on the local machine; it works also for remote machines:
library(progressr)
handlers("progress", global = TRUE)
library(foreach)
library(futurize)
plan(multisession, workers = 2)
my_fcn <- function(xs) {
p <- progressor(along = xs)
foreach(x = xs) %do% {
Sys.sleep((10.0-x)/2)
p(sprintf("x=%g", x))
sqrt(x)
} |> futurize()
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40% x=2
Note that using the %dofuture% operator of the doFuture package,
or traditional %dopar% registered via registerDoFuture(), is still supported.
For example:
library(progressr)
handlers("progress", global = TRUE)
library(doFuture)
registerDoFuture() ## %dopar% parallelizes via future
plan(multisession, workers = 2)
my_fcn <- function(xs) {
p <- progressor(along = xs)
foreach(x = xs) %dopar% {
Sys.sleep((10.0-x)/2)
p(sprintf("x=%g", x))
sqrt(x)
}
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40% x=2
Here is an example that uses purrr::map() and futurize() to parallelize
on the local machine while at the same time signaling progression updates:
library(progressr)
handlers("progress", global = TRUE)
library(futurize)
plan(multisession, workers = 2)
my_fcn <- function(xs) {
p <- progressor(along = xs)
purrr::map(xs, function(x) {
Sys.sleep((10.0-x)/2)
p(sprintf("x=%g", x))
sqrt(x)
}) |> futurize()
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40% x=2
Note: Using future_map() of the furrr package is still supported.
This solution does not involve the .progress = TRUE
argument that furrr implements. Because progressr is more
generic and because .progress = TRUE only supports certain future
backends and produces errors on non-supported backends, I recommend
to stop using .progress = TRUE and use the progressr package
instead.
Here is an example that uses bplapply() of the BiocParallel
package and futurize() to parallelize on the local machine while
at the same time signaling progression updates:
library(progressr)
handlers("progress", global = TRUE)
library(BiocParallel)
library(futurize)
plan(multisession, workers = 2)
my_fcn <- function(xs) {
p <- progressor(along = xs)
bplapply(xs, function(x) {
Sys.sleep((10.0-x)/2)
p(sprintf("x=%g", x))
sqrt(x)
}) |> futurize()
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40% x=2
Here is an example that uses llply() of the plyr package and
futurize() to parallelize on the local machine while at the same
time signaling progression updates:
library(progressr)
handlers("progress", global = TRUE)
library(plyr)
library(futurize)
plan(multisession, workers = 2)
my_fcn <- function(xs) {
p <- progressor(along = xs)
llply(xs, function(x, ...) {
Sys.sleep((10.0-x)/2)
p(sprintf("x=%g", x))
sqrt(x)
}) |> futurize()
}
y <- my_fcn(1:10)
# / [================>-----------------------------] 40% x=2
Note: As an alternative to the above, recommended approach, one can
use .progress = "progressr" together with .parallel = TRUE and the doFuture package. This
requires plyr (>= 1.8.7).
As of August 2025, there are six types of future backends that are known(*) to provide near-live progress updates:
sequential,multicore,multisession, andcluster (local and remote)future.callr::callrfuture.mirai::mirai_multisessionHere "near-live" means that the progress handlers will report on
progress almost immediately when the progress is signaled on the
worker. This is because these parallel backends handle the special
condition class immediateCondition - they detect when such
conditions are signaled and relay them to the parent R process as soon
as possible. For all other future backends, the progress updates are
only relayed back to the main machine and reported together with the
results of the futures. For instance, if lapply(X, FUN) |> futurize()
chunks up the processing of, say, 100 elements in X into eight
futures, we will see progress from each of the 100 elements as they
are done when using a future backend supporting "near-live" updates,
whereas we will only see those updates flushed eight times when
using any other types of future backends.
(*) Other future backends may gain support for "near-live" progress updating later. Adding support for those is independent of the progressr package. Feature requests for adding that support should go to those future-backend packages.