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?
EDIT: another version that preserves original types
EDIT 2: according to comments of Tomas Petricek
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 ofIEvent
(so the name of the method isAwaitObservable
). There are some serious memory leaks when usingEvent.merge
(and other combinators from theEvent
module) together withAwaitEvent
, so you should useObservable.merge
etc. andAwaitObservable
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 usingAwaitEvent
, 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:
BTW: You may also want to look at this article (based on chapter 16 from my book).
You can use a combination of
Event.map
andEvent.merge
:Then you can use
Async.AwaitEvent
with this new event. IfMouseMove
andKeyDown
had the same type, you could skip theEvent.map
step and just directly merge them.EDIT
But at Tomas points out, you should use the
Observable
combinators in preference to theEvent
ones.In the interest of understanding what's going on I looked up the source code to Event.map, Event.merge and Choice.
This means our solution is creating 3 new events.
We could reduce this to one event by making a tightly coupled version of this library code.
And here is our new code.