In F# I know how to wait asynchronously for one event using Async.AwaitEvent
:
let test = async {
let! move = Async.AwaitEvent(form.MouseMove)
...handle move... }
Suppose I want to wait for either the MouseMove
or the KeyDown
event. I'd like to have something like this:
let! moveOrKeyDown = Async.AwaitEvent(form.MouseMove, form.KeyDown)
This function doesn't exist but is there another way to do this?
let ignoreEvent e = Event.map ignore e
let merged = Event.merge (ignoreEvent f.KeyDown) (ignoreEvent f.MouseMove)
Async.AwaitEvent merged
EDIT: another version that preserves original types
let merged = Event.merge (f.KeyDown |> Event.map Choice1Of2) (f.MouseMove |> Event.map Choice2Of2)
Async.AwaitEvent merged
EDIT 2: according to comments of Tomas Petricek
let e1 = f.KeyDown |> Observable.map Choice1Of2
let e2 = f.MouseMove |> Observable.map Choice2Of2
let! evt = Observable.merge e1 e2 |> Async.AwaitObservable
AwaitObservable primitive can be taken from here ('Reactive demos in Silverlight' by Tomas Petricek).
I used an implementation of a method that you use in your sample in the talk about reactive programming that I had in London (there is a download link at the bottom of the page). If you're interested in this topic, you may find the talk useful as well :-).
The version I'm using takes IObservable
instead of IEvent
(so the name of the method is AwaitObservable
). There are some serious memory leaks when using Event.merge
(and other combinators from the Event
module) together with AwaitEvent
, so you should use Observable.merge
etc. and AwaitObservable
instead.
The problem is described in more detail here (see Section 3 for a clear example). Briefly - when you use Event.merge
, it attaches a handler to the source event (e.g. MouseDown
), but it does not remove the handler after you finish waiting using AwaitEvent
, so the event is never removed - if you keep waiting in a loop coded using asynchronous workflow, you keep adding new handlers (that do not do anything when run).
A simple correct solution (based on what desco posted) would look like this:
let rec loop () = async {
let e1 = f.KeyDown |> Observable.map Choice1Of2
let e2 = f.MouseMove |> Observable.map Choice2Of2
let! evt = Observable.merge e1 e2 |> Async.AwaitObservable
// ...
return! loop() } // Continue looping
BTW: You may also want to look at this article (based on chapter 16 from my book).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With