Package 'later'

Title: Utilities for Scheduling Functions to Execute Later with Event Loops
Description: Executes arbitrary R or C functions some time after the current time, after the R execution stack has emptied. The functions are scheduled in an event loop.
Authors: Winston Chang [aut, cre], Joe Cheng [aut], Posit Software, PBC [cph], Marcus Geelnard [ctb, cph] (TinyCThread library, https://tinycthread.github.io/), Evan Nemerson [ctb, cph] (TinyCThread library, https://tinycthread.github.io/)
Maintainer: Winston Chang <winston@posit.co>
License: MIT + file LICENSE
Version: 1.3.2
Built: 2024-11-02 05:13:10 UTC
Source: https://github.com/r-lib/later

Help Index


Private event loops

Description

Normally, later uses a global event loop for scheduling and running functions. However, in some cases, it is useful to create a private event loop to schedule and execute tasks without disturbing the global event loop. For example, you might have asynchronous code that queries a remote data source, but want to wait for a full back-and-forth communication to complete before continuing in your code – from the caller's perspective, it should behave like synchronous code, and not do anything with the global event loop (which could run code unrelated to your operation). To do this, you would run your asynchronous code using a private event loop.

Usage

create_loop(parent = current_loop(), autorun = NULL)

destroy_loop(loop)

exists_loop(loop)

current_loop()

with_temp_loop(expr)

with_loop(loop, expr)

global_loop()

Arguments

parent

The parent event loop for the one being created. Whenever the parent loop runs, this loop will also automatically run, without having to manually call run_now() on this loop. If NULL, then this loop will not have a parent event loop that automatically runs it; the only way to run this loop will be by calling run_now() on this loop.

autorun

This exists only for backward compatibility. If set to FALSE, it is equivalent to using parent=NULL.

loop

A handle to an event loop.

expr

An expression to evaluate.

Details

create_loop creates and returns a handle to a private event loop, which is useful when for scheduling tasks when you do not want to interfere with the global event loop.

destroy_loop destroys a private event loop.

exists_loop reports whether an event loop exists – that is, that it has not been destroyed.

current_loop returns the currently-active event loop. Any calls to later() or run_now() will use the current loop by default.

with_loop evaluates an expression with a given event loop as the currently-active loop.

with_temp_loop creates an event loop, makes it the current loop, then evaluates the given expression. Afterwards, the new event loop is destroyed.

global_loop returns a handle to the global event loop.


Executes a function later

Description

Schedule an R function or formula to run after a specified period of time. Similar to JavaScript's setTimeout function. Like JavaScript, R is single-threaded so there's no guarantee that the operation will run exactly at the requested time, only that at least that much time will elapse.

Usage

later(func, delay = 0, loop = current_loop())

Arguments

func

A function or formula (see rlang::as_function()).

delay

Number of seconds in the future to delay execution. There is no guarantee that the function will be executed at the desired time, but it should not execute earlier.

loop

A handle to an event loop. Defaults to the currently-active loop.

Details

The mechanism used by this package is inspired by Simon Urbanek's background package and similar code in Rhttpd.

Value

A function, which, if invoked, will cancel the callback. The function will return TRUE if the callback was successfully cancelled and FALSE if not (this occurs if the callback has executed or has been cancelled already).

Note

To avoid bugs due to reentrancy, by default, scheduled operations only run when there is no other R code present on the execution stack; i.e., when R is sitting at the top-level prompt. You can force past-due operations to run at a time of your choosing by calling run_now().

Error handling is not particularly well-defined and may change in the future. options(error=browser) should work and errors in func should generally not crash the R process, but not much else can be said about it at this point. If you must have specific behavior occur in the face of errors, put error handling logic inside of func.

Examples

# Example of formula style
later(~cat("Hello from the past\n"), 3)

# Example of function style
later(function() {
  print(summary(cars))
}, 2)

Relative time to next scheduled operation

Description

Returns the duration between now and the earliest operation that is currently scheduled, in seconds. If the operation is in the past, the value will be negative. If no operation is currently scheduled, the value will be Inf.

Usage

next_op_secs(loop = current_loop())

Arguments

loop

A handle to an event loop.


Execute scheduled operations

Description

Normally, operations scheduled with later() will not execute unless/until no other R code is on the stack (i.e. at the top-level). If you need to run blocking R code for a long time and want to allow scheduled operations to run at well-defined points of your own operation, you can call run_now() at those points and any operations that are due to run will do so.

Usage

run_now(timeoutSecs = 0L, all = TRUE, loop = current_loop())

Arguments

timeoutSecs

Wait (block) for up to this number of seconds waiting for an operation to be ready to run. If 0, then return immediately if there are no operations that are ready to run. If Inf or negative, then wait as long as it takes (if none are scheduled, then this will block forever).

all

If FALSE, run_now() will execute at most one scheduled operation (instead of all eligible operations). This can be useful in cases where you want to interleave scheduled operations with your own logic.

loop

A handle to an event loop. Defaults to the currently-active loop.

Details

If one of the callbacks throws an error, the error will not be caught, and subsequent callbacks will not be executed (until run_now() is called again, or control returns to the R prompt). You must use your own tryCatch if you want to handle errors.

Value

A logical indicating whether any callbacks were actually run.