run_worker: Background workers and subprocesses

View source: R/workers.R

run_workerR Documentation

Background workers and subprocesses

Description

app$run_worker() runs a function in a separate R process and reports back through events and callbacks, so the interface stays responsive during long computations (model fitting, downloads, file processing). app$run_process() does the same for an external program.

Usage

run_worker(
  fn,
  args = list(),
  on_complete = NULL,
  on_error = NULL,
  on_progress = NULL,
  name = NULL,
  owner = NULL,
  packages = character(),
  inline = FALSE,
  on_stdout = NULL,
  on_stderr = NULL,
  timeout = NULL,
  app = current_app()
)

run_process(
  command,
  args = character(),
  on_complete = NULL,
  on_error = NULL,
  on_stdout = NULL,
  on_stderr = NULL,
  on_exit = NULL,
  timeout = NULL,
  wd = NULL,
  env = NULL,
  name = NULL,
  owner = NULL,
  app = current_app()
)

Arguments

fn

The function to run.

args

A list of arguments.

on_complete

⁠function(result, app)⁠.

on_error

⁠function(message, app)⁠.

on_progress

⁠function(value, message, app)⁠.

name

Name of the worker.

owner

A widget owning the worker (events come from it, and it is cancelled when the widget is removed).

packages

Packages to attach in the worker process.

inline

Run in this process instead (for tests).

on_stdout, on_stderr

⁠function(line, app)⁠ called for each output line.

timeout

Seconds after which the worker is stopped and fails (NULL: no limit).

app

The app (defaults to the running app).

command

The program to run (a path or a name found on the PATH).

on_exit

⁠function(status, app)⁠ called when the process ends (whatever the status).

wd

Working directory (NULL: the current one).

env

Named character vector of extra environment variables.

Details

The function runs in a fresh R session (Rscript --vanilla), so it cannot directly access the caller's global environment. Lexical values referenced by the function are captured by value in a small environment; unrelated bindings in the enclosing environment are omitted. For example, a function using a local scalar binding works without serializing other locals. Referenced nested and recursive helper functions are captured too. Dynamic lexical lookup (get(), eval() or parent.frame()) and active bindings are rejected; pass those values explicitly through args. Prefer args for large data and list required packages in packages. Inside the function, termr_progress(value, message) reports progress on a channel of its own, so anything the function prints to stdout or stderr is free for on_stdout / on_stderr (message() goes to stderr). The child receives the parent's library search paths, and R_TESTS is cleared so a check-session startup hook cannot rerun the test suite.

run_process(command, args) starts a program without a shell: the command and its arguments are separate, so nothing is interpolated or interpreted. (To use a shell on purpose, run it yourself: run_process("sh", c("-c", script)).) It completes with the exit status as result; a non-zero status counts as failure.

Events (from the owner widget, or the screen), with event$data$worker: "worker.started", "worker.progress" (value, message), "worker.stdout" / "worker.stderr" (line), "worker.completed" (result), "worker.failed" (error, timed_out, status), "worker.cancelled". Output lines are stripped of control characters; at most 200 lines are delivered per event-loop tick.

Workers stop when cancel() is called, when their owner widget is removed, when timeout seconds have passed, and when the app exits. A stopping worker is interrupted first (Unix) and killed, with its child processes, if it does not end within a short grace period.

Temporary files (the job, the result and the progress file) have unique names and are removed when the worker ends, however it ends.

The job (the function with its referenced lexical bindings, and args) is serialized to a temporary file. Explicitly referenced objects and values in args are still serialized. Workers and programs inherit the app's environment variables: avoid putting secrets into worker closures or arguments unnecessarily.

There is no worker pool: start as many workers as you need; each is an independent process. This API is experimental.

Value

A Worker handle with cancel(), state, result, error, stdout and stderr.

Testing

inline = TRUE runs the function in the current R process at once (blocking), which makes tests fast and deterministic. With real processes, pilot$wait_for_workers() waits until they finish.

Examples

pilot <- test_app(app(label("Jobs")), width = 30, height = 1)
# `inline = TRUE` runs the job in this process; without it, the same call
# starts a background R process, where `termr_progress()` is available.
pilot$app$run_worker(
  function(n) sum(seq_len(n)),
  args = list(n = 20),
  inline = TRUE,
  on_complete = function(result, app) message("result: ", result)
)
pilot$step()
pilot$stop()

termr documentation built on Oct. 21, 2026, 5:13 p.m.