首页 > 解决方案 > 异步方法中的类型不匹配

问题描述

我正在编写一个异步方法,它应该异步查询端口,直到找到一个端口,或者在 5 分钟后超时;

    member this.GetPort(): Async<Port> = this._GetPort(DateTime.Now)

    member this._GetPort(startTime: DateTime): Async<Port> = async {
        match this._TryGetOpenPort() with
        | Some(port) -> port
        | None -> do
            if (DateTime.Now - startTime).TotalMinutes >= 5 then
                raise (Exception "Unable to open a port")
            else
                do! Async.Sleep(100)
                let! result = this._GetPort(startTime)
                result}

    member this._TryGetOpenPort(): Option<Port> = 
        // etc.

但是,我在 ; 中遇到了一些奇怪的类型不一致_GetPort。该函数说我正在返回一个类型Async<unit>而不是Async<Port>.

标签: asynchronoustypesf#timeoutmatching

解决方案


这有点不直观,但让你的代码工作的方法是这样的:

member private this.GetPort(startTime: DateTime) =
    async {
        match this.TryGetOpenPort() with
        | Some port ->
            return port
        | None ->
            if (DateTime.Now - startTime).TotalMinutes >= 5 then
                raise (Exception "Unable to open a port")
            
            do! Async.Sleep(100)
            let! result = this.GetPort(startTime)
            return result
    }

member private this.TryGetOpenPort() = failwith "yeet" // TODO

我冒昧地清理了一些东西并将成员设为私有,因为这似乎是您在这里主要追求的内容,并以更详细的内部方式获取端口。

您的代码未编译的原因是因为您return从计算中得到的内容不一致:

  • 如果Some(port)您缺少return关键字 - 需要将值提升回Async<port>
  • if提出异常的表达式有一个else分支,但你不是return来自两者。在这种情况下,由于您显然不希望返回任何内容而只是引发异常,因此您可以省略else并使其成为命令式程序流,就像在非异步代码中一样。

您可能希望考虑的另一件事是如果抛出异常是您想要的,或者只是返回一个Result<T,Err>或一个选项是正确的调用。异常本质上并不坏,但如果有一种很好的方法可以将含义赋予包装返回值的类型,那么很多 F# 编程通常会导致避免使用它们。


推荐阅读