| Title: | Utilities for Scheduling Functions to Execute Later with EventLoops |
|---|---|
| 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] (ORCID: <https://orcid.org/0000-0002-1576-2126>), Joe Cheng [aut], Charlie Gao [aut, cre] (ORCID: <https://orcid.org/0000-0002-0750-061X>), Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>), Marcus Geelnard [ctb, cph] (TinyCThread library, https://tinycthread.github.io/), Evan Nemerson [ctb, cph] (TinyCThread library, https://tinycthread.github.io/) |
| Maintainer: | Charlie Gao <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.4.4.9000 |
| Built: | 2025-11-19 17:24:38 UTC |
| Source: | https://github.com/r-lib/later |
Normally, later uses a global event loop for scheduling and runningfunctions. However, in some cases, it is useful to create aprivateevent loop to schedule and execute tasks without disturbing the global eventloop. For example, you might have asynchronous code that queries a remotedata source, but want to wait for a full back-and-forth communication tocomplete before continuing in your code – from the caller's perspective, itshould behave like synchronous code, and not do anything with the globalevent loop (which could run code unrelated to your operation). To do this,you would run your asynchronous code using a private event loop.
create_loop(parent = current_loop())destroy_loop(loop)exists_loop(loop)current_loop()with_temp_loop(expr)with_loop(loop, expr)global_loop()create_loop(parent= current_loop())destroy_loop(loop)exists_loop(loop)current_loop()with_temp_loop(expr)with_loop(loop, expr)global_loop()
parent | The parent event loop for the one being created. Whenever theparent loop runs, this loop will also automatically run, without having tomanually call |
loop | A handle to an event loop. |
expr | An expression to evaluate. |
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 interferewith the global event loop.
destroy_loop destroys a private event loop.
exists_loop reports whether an event loop exists – that is, that ithas not been destroyed.
current_loop returns the currently-active event loop. Any calls tolater() orrun_now() will use the current loop bydefault.
with_loop evaluates an expression with a given event loop as thecurrently-active loop.
with_temp_loop creates an event loop, makes it the current loop, thenevaluates the given expression. Afterwards, the new event loop is destroyed.
global_loop returns a handle to the global event loop.
Schedule an R function or formula to run after a specified period of time.Similar to JavaScript'ssetTimeout function. Like JavaScript, R issingle-threaded so there's no guarantee that the operation will run exactlyat the requested time, only that at least that much time will elapse.
later(func, delay = 0, loop = current_loop())later(func, delay=0, loop= current_loop())
func | A function or formula (see |
delay | Number of seconds in the future to delay execution. There is noguarantee that the function will be executed at the desired time, but itshould not execute earlier. |
loop | A handle to an event loop. Defaults to the currently-active loop. |
The mechanism used by this package is inspired by Simon Urbanek'sbackground package and similar code inRhttpd.
A function, which, if invoked, will cancel the callback. Thefunction will returnTRUE if the callback was successfullycancelled andFALSE if not (this occurs if the callback hasexecuted or has been cancelled already).
To avoid bugs due to reentrancy, by default, scheduled operations only runwhen there is no other R code present on the execution stack; i.e., when R issitting at the top-level prompt. You can force past-due operations to run ata time of your choosing by callingrun_now().
Error handling is not particularly well-defined and may change in the future.options(error=browser) should work and errors infunc should generally notcrash 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 errorhandling logic inside offunc.
# Example of formula stylelater(~cat("Hello from the past\n"), 3)# Example of function stylelater(function() { print(summary(cars))}, 2)# Example of formula stylelater(~cat("Hello from the past\n"),3)# Example of function stylelater(function(){ print(summary(cars))},2)
Schedule an R function or formula to run after an indeterminate amount oftime when file descriptors are ready for reading or writing, subject to anoptional timeout.
later_fd( func, readfds = integer(), writefds = integer(), exceptfds = integer(), timeout = Inf, loop = current_loop())later_fd( func, readfds= integer(), writefds= integer(), exceptfds= integer(), timeout=Inf, loop= current_loop())
func | A function that takes a single argument, a logical vector thatindicates which file descriptors are ready (a concatenation of |
readfds | Integer vector of file descriptors, or Windows SOCKETs, tomonitor for being ready to read. |
writefds | Integer vector of file descriptors, or Windows SOCKETs, tomonitor being ready to write. |
exceptfds | Integer vector of file descriptors, or Windows SOCKETs, tomonitor for error conditions pending. |
timeout | Number of seconds to wait before giving up, and calling |
loop | A handle to an event loop. Defaults to the currently-active loop. |
On the occasion the system-levelpoll (on WindowsWSAPoll) returns anerror, the callback will be made on a vector of allNAs. This isindistinguishable from a case where thepoll succeeds but there are errorconditions pending against each file descriptor.
If no file descriptors are supplied, the callback is scheduled for immediateexecution and made on the empty logical vectorlogical(0).
A function, which, if invoked, will cancel the callback. Thefunction will returnTRUE if the callback was successfullycancelled andFALSE if not (this occurs if the callback hasexecuted or has been cancelled already).
To avoid bugs due to reentrancy, by default, scheduled operations only runwhen there is no other R code present on the execution stack; i.e., when R issitting at the top-level prompt. You can force past-due operations to run ata time of your choosing by callingrun_now().
Error handling is not particularly well-defined and may change in the future.options(error=browser) should work and errors infunc should generally notcrash 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 errorhandling logic inside offunc.
# create nanonext socketss1 <- nanonext::socket(listen = "inproc://nano")s2 <- nanonext::socket(dial = "inproc://nano")fd1 <- nanonext::opt(s1, "recv-fd")fd2 <- nanonext::opt(s2, "recv-fd")# 1. timeout: prints FALSE, FALSElater_fd(print, c(fd1, fd2), timeout = 0.1)Sys.sleep(0.2)run_now()# 2. fd1 ready: prints TRUE, FALSElater_fd(print, c(fd1, fd2), timeout = 1)res <- nanonext::send(s2, "msg")Sys.sleep(0.1)run_now()# 3. both ready: prints TRUE, TRUEres <- nanonext::send(s1, "msg")later_fd(print, c(fd1, fd2), timeout = 1)Sys.sleep(0.1)run_now()# 4. fd2 ready: prints FALSE, TRUEres <- nanonext::recv(s1)later_fd(print, c(fd1, fd2), timeout = 1)Sys.sleep(0.1)run_now()# 5. fds invalid: prints NA, NAclose(s2)close(s1)later_fd(print, c(fd1, fd2), timeout = 0)Sys.sleep(0.1)run_now()# create nanonext socketss1<- nanonext::socket(listen="inproc://nano")s2<- nanonext::socket(dial="inproc://nano")fd1<- nanonext::opt(s1,"recv-fd")fd2<- nanonext::opt(s2,"recv-fd")# 1. timeout: prints FALSE, FALSElater_fd(print, c(fd1, fd2), timeout=0.1)Sys.sleep(0.2)run_now()# 2. fd1 ready: prints TRUE, FALSElater_fd(print, c(fd1, fd2), timeout=1)res<- nanonext::send(s2,"msg")Sys.sleep(0.1)run_now()# 3. both ready: prints TRUE, TRUEres<- nanonext::send(s1,"msg")later_fd(print, c(fd1, fd2), timeout=1)Sys.sleep(0.1)run_now()# 4. fd2 ready: prints FALSE, TRUEres<- nanonext::recv(s1)later_fd(print, c(fd1, fd2), timeout=1)Sys.sleep(0.1)run_now()# 5. fds invalid: prints NA, NAclose(s2)close(s1)later_fd(print, c(fd1, fd2), timeout=0)Sys.sleep(0.1)run_now()
Returns the duration between now and the earliest operation that is currentlyscheduled, in seconds. If the operation is in the past, the value will benegative. If no operation is currently scheduled, the value will beInf.
next_op_secs(loop = current_loop())next_op_secs(loop= current_loop())
loop | A handle to an event loop. |
Normally, operations scheduled withlater() will not execute unless/untilno other R code is on the stack (i.e. at the top-level). If you need to runblocking R code for a long time and want to allow scheduled operations to runat well-defined points of your own operation, you can callrun_now() atthose points and any operations that are due to run will do so.
run_now(timeoutSecs = 0L, all = TRUE, loop = current_loop())run_now(timeoutSecs=0L, all=TRUE, loop= current_loop())
timeoutSecs | Wait (block) for up to this number of seconds waiting foran operation to be ready to run. If |
all | If |
loop | A handle to an event loop. Defaults to the currently-active loop. |
If one of the callbacks throws an error, the error willnot be caught, andsubsequent callbacks will not be executed (untilrun_now() is called again,or control returns to the R prompt). You must use your owntryCatch if you want to handle errors.
A logical indicating whether any callbacks were actually run.