Open mmuurr opened 2 years ago
If you want it to run, you need to call run_now()
within the with_temp_loop()
, like this:
library(later)
with_temp_loop({
later(function() print("foo"), delay = 1)
run_now(2)
})
The temporary loop exists until the with_temp_loop()
function exits.
If you want the loop to persist, you need to do something like this:
loop <- create_loop()
with_loop(loop, later(function() print("foo"), delay = 2))
In this case, the loop will exist until it is garbage collected, or you call destroy_loop(loop)
. It can be GC'd when it is empty and there are no more references to loop
. Note that if parent
is not NULL
, the parent loop will keep a reference to the child loop as long there are any functions in it.
I decided to test to make what I'm saying is correct; here's the code:
library(later)
# A quiet wrapper for gc().
gc <- function() invisible(base::gc())
# A function for checking whether a loop exists, by id. This is so that we don't
# need to keep a reference to the full loop object which holds a reference to
# the underlying data structure and will prevent it from being destroyed.
# Instead, we just need the loop ID.
loop_exists <- function(id) later::exists_loop(list(id = id))
make_f <- function() {
reg.finalizer(
environment(),
function(e) { print("finalized") }
)
function() { print("run") }
}
# Sanity check: creating the function and GC'ing it will cause the finalizer to
# run.
make_f()
NULL # Need to run this at console, so the function isn't saved in .Last.value
gc()
#> [1] "finalized"
# Creating a private loop, scheduling the callback on it, and running the global
# loop with run_now(): will cause the callback to run.
loop <- create_loop()
with_loop(loop, later(make_f(), 2))
run_now(2)
#> [1] "run"
gc()
#> [1] "finalized"
# Creating a private loop, scheduling the callback on it, and then destroying
# the private loop: the callback won't run and can be GC'd.
loop <- create_loop()
with_loop(loop, later(make_f(), 2))
destroy_loop(loop)
gc()
#> [1] "finalized"
# Creating a private loop, scheduling the callback on it, and then removing the
# var referencing the loop: the loop and callback still exist and will run. This
# is because the global loop keeps a reference to the private loop (until it is
# emptied).
loop <- create_loop()
(loop_id <- loop$id)
#> [1] 3
with_loop(loop, later(make_f(), 2))
rm(loop)
gc()
loop_exists(loop_id)
#> [1] TRUE
run_now(2)
#> [1] "run"
loop_exists(loop_id)
#> [1] FALSE
gc()
#> [1] "finalized"
# With parent=NULL, the loop (and objects in it) will be finalized after there
# are no more references to it -- it doesn't have to wait until the loop is
# empty.
loop <- create_loop(parent = NULL)
(loop_id <- loop$id)
#> [1] 4
with_loop(loop, later(make_f(), 2))
rm(loop)
loop_exists(loop_id)
#> [1] TRUE
gc() # Loop is destroyed on first GC
loop_exists(loop_id)
#> [1] FALSE
gc() # Callback env is finalized on second GC
#> [1] "finalized"
@wch aaaahhhh that makes sense, I I also assume, then, that something like this (swapping the delay & timeout values in your example) would be an anti-pattern preventing execution:
with_temp_loop({
later(function() print("foo"), delay = 2)
run_now(1) ## too soon, as the `later` function isn't ready yet.
})
If I gin up some examples (using some of your examples from this thread), would it be worth me adding to the documentation and submitting a PR (or do y'all prefer to write your own documentation internally)? In either case, thanks for the thoughtful reply, this helped me a lot!
Yes, thanks, that would be good to add in the documentation! If you are feeling ambitious, a new page about private event loops would be a great thing to have.
I'm probably missing something very obvious here, but I'm also completed stumped as to how to use
with_temp_loop()
. Here's a trivial global-loop example:Works great; prints "foo" after a few seconds.
Now I wanted to pop this onto a different (i.e. non-global) loop ... one way is to formally create a loop and use that loop's reference. Per the documentation, for a one-time-only later execution we can also do something like this(?):
But this never seems to execute. And since temp loops are created with
parent=NULL
(and thus the global loop'srun_now()
doesn't trigger this temp loop to run), it's not obvious to me how to force execution withrun_now()
(since we don't have a reference to the loop).Perhaps related to rstudio/httpuv#250?