3

是否可以在邮箱处理器上等待,以下代码在 F# 交互中工作,但有没有办法在应用程序或单元测试中等待它?

[<TestMethod>]
member this.TestMailboxProcessor() =
    let mailboxProcessor = MailboxProcessor<string>.Start(fun inbox ->
        async {
            while true do
            let! msg = inbox.Receive()
            printfn "agent got message %s" msg // too late, UnitTest exits
        }
    )

    mailboxProcessor.Post "ping"
    Console.WriteLine "message posted" // I see this in the console
    Assert.IsTrue(true)
4

2 回答 2

5

在这种情况下是不可能的,但是您可以定义您的消息类型以包含AsyncReplyChannel<'t>,这样您就可以使用MailboxProcessor.PostAndReply而不是 Post。这样,调用代码可以(同步或异步)等待响应值,或至少等待处理完成的指示。

您修改后的源代码可能如下所示:

[<TestMethod>]
member this.TestMailboxProcessor() =
    let mailboxProcessor =
        MailboxProcessor<string * AsyncReplyChannel<unit>>.Start(fun inbox ->
            async {
                while true do
                let! msg, replyChannel = inbox.Receive()
                printfn "agent got message %s" msg
                (* 
                  Reply takes a value of the generic param of
                  AsyncReplyChannel<'t>, in this case just a unit
                *)
                replyChannel.Reply()
            }
        )

    (*
      You can't create an AsyncReplyChannel<'t> value, but this does it for you.
      Also always, always use timeouts when awaiting message replies. 
    *)
    mailboxProcessor.PostAndReply(
        (fun replyChannel -> "ping", replyChannel),
        timeout = 1000)
    (* This gets printed only after the message has been posted and processed *)
    Console.WriteLine "message posted"
    Assert.IsTrue(true)

MailboxProcessors 是一个有点棘手的话题,所以确保你总是使用超时,否则如果你的代码出错,或者异常终止消息循环,你的代码将永远挂起。在测试中不好,在生产中更糟。

于 2018-04-03T01:11:29.297 回答
2

您应该使用PostAndAsyncReplyor PostAndReply(阻止版本)

let replyAgent = MailboxProcessor.Start(fun inbox ->
    let rec loop() = 
        async {
            let! (replyChannel: AsyncReplyChannel<_>), msg = inbox.Receive()
            replyChannel.Reply (sprintf "replied for message: %A" msg)
            return! loop()
        }
    loop() )

let reply = replyAgent.PostAndReply(fun replCh -> replCh, "Hi")
printfn "%s" reply //prints "replied for message: Hi"
于 2018-04-03T01:07:19.433 回答