我正在尝试通过编写连接到Web套接字的程序来学习F#中的Observable模块,监听消息,然后在基于Observables的一组流中处理它们。但是,我很难理解实际行为。
首先,我设置了一个这样的Web套接字:
open System
open System.Net.WebSockets
open System.Threading
let connectFeed =
let feedUrl = "blah blah"
let buffer : byte array = Array.zeroCreate 1024
let segment = ArraySegment(buffer)
let socketEvent = new Event<string>()
let task = async {
let random = Random(DateTime.Now.Millisecond)
use socket = new ClientWebSocket()
let! token = Async.CancellationToken
do! Async.AwaitTask (socket.ConnectAsync(Uri(feedUrl), token))
while not token.IsCancellationRequested do
let! result = Async.AwaitTask (socket.ReceiveAsync(segment, token))
socketEvent.Trigger (Encoding.UTF8.GetString(buffer))
Array.fill buffer 0 buffer.Length 0uy
}
(task, socketEvent.Publish)
let deserializeMsg (raw:string) =
// returns a MsgType based on the received message
let tryGetData (msg:MsgType) =
// returns Some data for specific kind of message; None otherwise
[<EntryPoint>]
let main argv =
let feedProc, feedStream = connectFeed
let msgStream = feedStream |> Observable.map deserializeMsg
msgStream |> Observable.subscribe (fun m -> printfn "got msg: %A" m) |> ignore
let dataStream = feedStream |> Observable.choose tryGetData
dataStream |> Observable.subscribe (fun d -> printfn "got data: %A" d) |> ignore
Async.RunSynchronously feedProc
0
我期待看到如下打印输出:
got msg: { some: "field" }
got msg: { some: "other" }
got msg: { some: "data" }
got data: { // whatever }
got msg: ...
...
相反,只有“收到消息”消息才会显示,即使有消息会导致tryGetData
返回Some
。
这里发生了什么?如何从单个事件中设置多个Observable
流?
更新:我已用此更新了我的代码:
let isMsgA msg =
printfn "isMsgA"
match msg with
| MsgA -> true // where MsgA is a member of a DU defined elsewhere, and is the result of deserializeMsg
| _ -> false
let isStringMsgA msg =
printfn "isStringMsgA"
if msg.StartsWith("{ \"type\": \"msga\"") then true else false
[<EntryPoint>]
let main argv =
let feedProc, feedStream = connectFeed
let msgStream = feedStream |> Observable.map deserializeMsg
msgStream
|> Observable.filter isMsgA
|> Observable.subscribe (fun m -> printfn "got msg MsgA")
|> ignore
feedStream
|> Observable.filter isStringMsgA
|> Observable.subscribe (fun m -> printfn "got string MsgA")
|> ignore
我得到一个充满“isStringMsgA”和“得到字符串MsgA”消息的屏幕,但是“isMsgA”和“得到msg MsgA”中只有一个消息。
我很困惑。
这是一个精简,可重现的例子,适合任何有趣的人: https://github.com/aggieben/test-observable
更新2 :看起来我可能会因为deserializeMsg
函数中抛出异常而看到此行为。还在挖......
答案 0 :(得分:2)
我没有看到为什么会发生这种情况的任何明显原因 - 您是否可以向tryGetData
添加一些日志记录以检查它获得的输入以及返回的结果?
使用Observable
模块时,您构建了处理管道的描述,Observable.subscribe
创建了一个具体的侦听器链,用于执行工作并将处理程序附加到主事件源。但是,这些事件并没有被消费掉#34; - 应将它们发送给所有观察员。
例如,尝试使用以下最小的演示:
let evt = Event<int>()
let e1 = evt.Publish |> Observable.choose (fun n ->
if n % 2 = 0 then Some "woop!" else None)
let e2 = evt.Publish |> Observable.map (fun n -> n * 10)
e1 |> Observable.subscribe (printfn "E1: %s")
e2 |> Observable.subscribe (printfn "E2: %d")
evt.Trigger(1)
evt.Trigger(2)
如果你运行它,它会打印出预期的结果:
E2: 10
E1: woop!
E2: 20