at time
is run_at time ignore ()
. after time
is run_after time ignore ()
.
You should generally prefer to use the run_*
functions, which allow one to synchronously update state via a user-supplied function when the event transitions to Happened
. That is, there is an important difference between:
let t = run_at time f ()
and:
let t = at time in
fired t
>>> function
| Happened () -> f ()
| Aborted () -> ()
With run_at
, if status t = Happened
, one knows that f
has run. With at
and fired
, one does not know whether f
has yet run; it may still be scheduled to run. Thus, with at
and fired
, it is easy to introduce a race. For example, consider these two code snippets:
let t = Event.after (sec 2.) in
upon (Event.fired t) (function
| Aborted () -> ()
| Happened () -> printf "Timer fired");
upon deferred_event (fun () ->
match Event.abort t () with
| Ok -> printf "Event occurred"
| Previously_aborted () -> assert false
| Previously_happened () -> printf "Event occurred after timer fired");
let t = Event.run_after (sec 2.) printf "Timer fired" in
upon deferred_event (fun () ->
match Event.abort t () with
| Ok -> printf "Event occurred"
| Previously_aborted () -> assert false
| Previously_happened () -> printf "Event occurred after timer fired");
In both snippets, if Event.abort
returns Ok
, "Timer fired" is never printed. However, the first snippet might print "Event occurred after timer fired" and then "Timer fired". This confused ordering cannot happen with Event.run_after
.