在F#中同时等待多个事件的任何事件

Ron*_*erg 7 events f# asynchronous

在F#中,我知道如何使用Async.AwaitEvent以下方法异步等待一个事件:

let test = async {
  let! move = Async.AwaitEvent(form.MouseMove)
  ...handle move... }
Run Code Online (Sandbox Code Playgroud)

假设我想要等待MouseMove或者KeyDown事件.我想要这样的东西:

let! moveOrKeyDown = Async.AwaitEvent(form.MouseMove, form.KeyDown)
Run Code Online (Sandbox Code Playgroud)

这个功能不存在,但有另一种方法吗?

Tom*_*cek 12

我使用了你在样本中使用的方法的实现,我在伦敦讨论了反应式编程(页面底部有一个下载链接).如果您对此主题感兴趣,您可能会发现该演讲也很有用:-).

我正在使用的版本IObservable代替IEvent(所以方法的名称是AwaitObservable).使用(以及模块中的其他组合器)时会出现严重的内存泄漏,因此您应该使用等等. Event.mergeEventAwaitEventObservable.mergeAwaitObservable

这里将更详细地描述该问题(有关清晰示例,请参见第3节).简单地说 - 当你使用时Event.merge,它会将一个处理程序附加到源事件(例如MouseDown),但是在你等待使用后它不会删除处理程序AwaitEvent,所以事件永远不会被删除 - 如果你继续在使用异步工作流编码的循环中等待,你不断添加新的处理程序(运行时不做任何事情).

一个简单的正确解决方案(基于desco发布的内容)将如下所示:

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
Run Code Online (Sandbox Code Playgroud)

顺便说一句:你可能也想看看这篇文章(根据我的书中的第16章).


des*_*sco 11

let ignoreEvent e = Event.map ignore e

let merged = Event.merge (ignoreEvent f.KeyDown) (ignoreEvent f.MouseMove)
Async.AwaitEvent merged
Run Code Online (Sandbox Code Playgroud)

编辑:保留原始类型的另一个版本

let merged = Event.merge (f.KeyDown |> Event.map Choice1Of2) (f.MouseMove |> Event.map Choice2Of2)
Async.AwaitEvent merged
Run Code Online (Sandbox Code Playgroud)

编辑2:根据Tomas Petricek的评论

let e1 = f.KeyDown |> Observable.map Choice1Of2
let e2 = f.MouseMove |> Observable.map Choice2Of2
let! evt = Observable.merge e1 e2 |> Async.AwaitObservable
Run Code Online (Sandbox Code Playgroud)

AwaitObservable原语可以从这里获取(Tomas Petricek的"Silverlight中的Reactive demos").

  • 你能考虑改变代码使用`Observable`而不是`Event`吗?(在这种情况下使用`Event.xyz`会导致泄漏 - 有关详细信息,请参阅我的答案...) (3认同)