Wait for the F # event

In F #, I know how to wait asynchronously for a single event using Async.AwaitEvent :

 let test = async { let! move = Async.AwaitEvent(form.MouseMove) ...handle move... } 

Suppose I want to wait for either MouseMove or KeyDown . I would like to have something like this:

 let! moveOrKeyDown = Async.AwaitEvent(form.MouseMove, form.KeyDown) 

This function does not exist, but is there any other way to do this?

+7
asynchronous events f #
source share
4 answers
 let ignoreEvent e = Event.map ignore e let merged = Event.merge (ignoreEvent f.KeyDown) (ignoreEvent f.MouseMove) Async.AwaitEvent merged 

EDIT: another version that retains the original types

 let merged = Event.merge (f.KeyDown |> Event.map Choice1Of2) (f.MouseMove |> Event.map Choice2Of2) Async.AwaitEvent merged 

EDIT 2 : according to comments by Thomas Petrichek

 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 ("Jet Demonstrations in Silverlight" by Tomasz Petricek).

+11
source share

I used the implementation of the method that you use in your example in the talk about reactive programming that I had in London (there is a download link at the bottom of the page). If you are interested in this topic, you can find a useful topic :-).

The version used accepts IObservable instead of IEvent (therefore the name of the method is AwaitObservable ). When using Event.merge (and other combinators from the Event module) memory leak > with AwaitEvent , you should use Observable.merge , etc. And AwaitObservable .

The problem is described in more detail here (see section 3 for a clear example). In short - when using Event.merge it attaches a handler to the original event (for example, MouseDown ), but after waiting with AwaitEvent it does not remove the handler, so the event is never deleted - if you keep waiting in an encoded loop using an asynchronous workflow, you continue to add new handlers (which do nothing at startup).

A simple correct solution (based on what was published in desco) is as follows:

 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 can also see this article (based on chapter 16 of my book).

+12
source share

In order to understand what was going on, I searched for the source code for EventMap, Event.merge, and Choice.

 type Choice<'T1,'T2> = | Choice1Of2 of 'T1 | Choice2Of2 of 'T2 [<CompiledName("Map")>] let map f (w: IEvent<'Delegate,'T>) = let ev = new Event<_>() w.Add(fun x -> ev.Trigger(fx)); ev.Publish [<CompiledName("Merge")>] let merge (w1: IEvent<'Del1,'T>) (w2: IEvent<'Del2,'T>) = let ev = new Event<_>() w1.Add(fun x -> ev.Trigger(x)); w2.Add(fun x -> ev.Trigger(x)); ev.Publish 

This means that our solution creates 3 new events.

 async { let merged = Event.merge (f.KeyDown |> Event.map Choice1Of2) (f.MouseMove |> Event.map Choice2Of2) let! move = Async.AwaitEvent merged } 

We could reduce this to a single event by creating a tightly coupled version of this library code.

 type EventChoice<'T1, 'T2> = | EventChoice1Of2 of 'T1 | EventChoice2Of2 of 'T2 with static member CreateChoice (w1: IEvent<_,'T1>) (w2: IEvent<_,'T2>) = let ev = new Event<_>() w1.Add(fun x -> ev.Trigger(EventChoice1Of2 x)) w2.Add(fun x -> ev.Trigger(EventChoice2Of2 x)) ev.Publish 

And here is our new code.

 async { let merged = EventChoice.CreateChoice form.MouseMove form.KeyDown let! move = Async.AwaitEvent merged } 
+4
source share

You can use a combination of Event.map and Event.merge :

 let eventOccurs e = e |> Event.map ignore let mouseOrKey = Event.merge (eventOccurs frm.MouseMove) (eventOccurs frm.KeyDown) 

Then you can use Async.AwaitEvent with this new event. If MouseMove and KeyDown are of the same type, you can skip the Event.map step and simply merge them.

EDIT

But, according to Thomas, you should use Observable combinators, preferring Event .

0
source share

All Articles