11
11
12
12
.globals <- new.env(parent = emptyenv())
13
13
14
+ # ' Private event loops
15
+ # '
16
+ # ' Normally, later uses a global event loop for scheduling and running
17
+ # ' functions. However, in some cases, it is useful to create a \emph{private}
18
+ # ' event loop to schedule and execute tasks without disturbing the global event
19
+ # ' loop. For example, you might have asynchronous code that queries a remote
20
+ # ' data source, but want to wait for a full back-and-forth communication to
21
+ # ' complete before continuing in your code -- from the caller's perspective, it
22
+ # ' should behave like synchronous code, and not do anything with the global
23
+ # ' event loop (which could run code unrelated to your operation). To do this,
24
+ # ' you would run your asynchronous code using a private event loop.
25
+ # '
26
+ # ' \code{create_loop} creates and returns a handle to a private event loop,
27
+ # ' which is useful when for scheduling tasks when you do not want to interfere
28
+ # ' with the global event loop.
29
+ # '
30
+ # ' \code{destory_loop} destroys a private event loop.
31
+ # '
32
+ # ' \code{exists_loop} reports whether an event loop exists -- that is, that it
33
+ # ' has not been destroyed.
34
+ # '
35
+ # ' \code{current_loop} returns the currently-active event loop. Any calls to
36
+ # ' \code{\link{later}()} or \code{\link{run_now}()} will use the current loop by
37
+ # ' default.
38
+ # '
39
+ # ' \code{with_loop} evaluates an expression with a given event loop as the
40
+ # ' currently-active loop.
41
+ # '
42
+ # ' \code{with_temp_loop} creates an event loop, makes it the current loop, then
43
+ # ' evaluates the given expression. Afterwards, the new event loop is destroyed.
44
+ # '
45
+ # ' \code{global_loop} returns a handle to the global event loop.
46
+ # '
47
+ # '
48
+ # ' @param loop A handle to an event loop.
49
+ # ' @param expr An expression to evaluate.
50
+ # ' @rdname create_loop
51
+ # '
14
52
# ' @export
15
53
create_loop <- function () {
16
54
id <- .globals $ next_id
@@ -28,6 +66,7 @@ create_loop <- function() {
28
66
loop
29
67
}
30
68
69
+ # ' @rdname create_loop
31
70
# ' @export
32
71
destroy_loop <- function (loop ) {
33
72
if (identical(loop , global_loop())) {
@@ -39,16 +78,19 @@ destroy_loop <- function(loop) {
39
78
}
40
79
}
41
80
81
+ # ' @rdname create_loop
42
82
# ' @export
43
83
exists_loop <- function (loop ) {
44
84
existsCallbackRegistry(loop $ id )
45
85
}
46
86
87
+ # ' @rdname create_loop
47
88
# ' @export
48
89
current_loop <- function () {
49
90
.globals $ current_loop
50
91
}
51
92
93
+ # ' @rdname create_loop
52
94
# ' @export
53
95
with_temp_loop <- function (expr ) {
54
96
loop <- create_loop()
@@ -57,6 +99,7 @@ with_temp_loop <- function(expr) {
57
99
with_loop(loop , expr )
58
100
}
59
101
102
+ # ' @rdname create_loop
60
103
# ' @export
61
104
with_loop <- function (loop , expr ) {
62
105
if (! identical(loop , current_loop())) {
@@ -68,6 +111,7 @@ with_loop <- function(loop, expr) {
68
111
force(expr )
69
112
}
70
113
114
+ # ' @rdname create_loop
71
115
# ' @export
72
116
global_loop <- function () {
73
117
.globals $ global_loop
@@ -86,42 +130,43 @@ print.event_loop <- function(x, ...) {
86
130
87
131
88
132
# ' Executes a function later
89
- # '
133
+ # '
90
134
# ' Schedule an R function or formula to run after a specified period of time.
91
135
# ' Similar to JavaScript's `setTimeout` function. Like JavaScript, R is
92
136
# ' single-threaded so there's no guarantee that the operation will run exactly
93
137
# ' at the requested time, only that at least that much time will elapse.
94
- # '
138
+ # '
95
139
# ' The mechanism used by this package is inspired by Simon Urbanek's
96
140
# ' [background](https://github.com/s-u/background) package and similar code in
97
141
# ' Rhttpd.
98
- # '
142
+ # '
99
143
# ' @note
100
144
# ' To avoid bugs due to reentrancy, by default, scheduled operations only run
101
145
# ' when there is no other R code present on the execution stack; i.e., when R is
102
146
# ' sitting at the top-level prompt. You can force past-due operations to run at
103
147
# ' a time of your choosing by calling [run_now()].
104
- # '
148
+ # '
105
149
# ' Error handling is not particularly well-defined and may change in the future.
106
150
# ' options(error=browser) should work and errors in `func` should generally not
107
151
# ' crash the R process, but not much else can be said about it at this point.
108
152
# ' If you must have specific behavior occur in the face of errors, put error
109
153
# ' handling logic inside of `func`.
110
- # '
154
+ # '
111
155
# ' @param func A function or formula (see [rlang::as_function()]).
112
- # ' @param delay Number of seconds in the future to delay execution. There is no
113
- # ' guarantee that the function will be executed at the desired time, but it
156
+ # ' @param delay Number of seconds in the future to delay execution. There is no
157
+ # ' guarantee that the function will be executed at the desired time, but it
114
158
# ' should not execute earlier.
159
+ # ' @param loop A handle to an event loop. Defaults to the currently-active loop.
115
160
# '
116
161
# ' @examples
117
162
# ' # Example of formula style
118
163
# ' later(~cat("Hello from the past\n"), 3)
119
- # '
164
+ # '
120
165
# ' # Example of function style
121
166
# ' later(function() {
122
167
# ' print(summary(cars))
123
168
# ' }, 2)
124
- # '
169
+ # '
125
170
# ' @export
126
171
later <- function (func , delay = 0 , loop = current_loop()) {
127
172
f <- rlang :: as_function(func )
@@ -148,15 +193,16 @@ later <- function(func, delay = 0, loop = current_loop()) {
148
193
# ' @param all If `FALSE`, `run_now()` will execute at most one scheduled
149
194
# ' operation (instead of all eligible operations). This can be useful in cases
150
195
# ' where you want to interleave scheduled operations with your own logic.
151
- # '
196
+ # ' @param loop A handle to an event loop. Defaults to the currently-active loop.
197
+ # '
152
198
# ' @return A logical indicating whether any callbacks were actually run.
153
199
# '
154
200
# ' @export
155
201
run_now <- function (timeoutSecs = 0L , all = TRUE , loop = current_loop()) {
156
202
if (timeoutSecs == Inf ) {
157
203
timeoutSecs <- - 1
158
204
}
159
-
205
+
160
206
if (! is.numeric(timeoutSecs ))
161
207
stop(" timeoutSecs must be numeric" )
162
208
@@ -166,10 +212,11 @@ run_now <- function(timeoutSecs = 0L, all = TRUE, loop = current_loop()) {
166
212
}
167
213
168
214
# ' Check if later loop is empty
169
- # '
215
+ # '
170
216
# ' Returns true if there are currently no callbacks that are scheduled to
171
217
# ' execute in the present or future.
172
- # '
218
+ # '
219
+ # ' @inheritParams create_loop
173
220
# ' @keywords internal
174
221
# ' @export
175
222
loop_empty <- function (loop = current_loop()) {
@@ -182,6 +229,7 @@ loop_empty <- function(loop = current_loop()) {
182
229
# ' scheduled, in seconds. If the operation is in the past, the value will be
183
230
# ' negative. If no operation is currently scheduled, the value will be `Inf`.
184
231
# '
232
+ # ' @inheritParams create_loop
185
233
# ' @export
186
234
next_op_secs <- function (loop = current_loop()) {
187
235
nextOpSecs(loop $ id )
0 commit comments