【问题标题】:Extract value from Future using Cats使用 Cats 从 Future 中提取价值
【发布时间】:2020-02-24 13:15:17
【问题描述】:

我正在尝试为 GitHub 的 API 编写一个小型库。我正在尝试模拟Github4s's library 的行为,这里是这个库的一个例子:

val user1 = Github(accessToken).users.get("rafaparadela")
object ProgramEval {
    val u1 = user1.exec[Eval, HttpResponse[String]]().value
 }

import cats.implicits._
import github4s.GithubResponses.GHResult

ProgramEval.u1 match {
  // Here the actual value of the request is returned, 
  // not the same as Future's onComplete, where the return type is Unit
  case Right(GHResult(result, status, headers)) => result.login
  case Left(e) => e.getMessage
}

我引用文档:

每个 Github4s API 调用都会返回一个 GHIO[GHResponse[A]],它是 Free[Github4s, GHResponse[A]] 的别名。

GHResponse[A] 是 Either[GHException, GHResult[A]] 的类型别名。

GHResult 包含 Github 给出的结果 A 以及响应的状态码和标头:

在某些时候,他们正在使用HttpClient.scala 制作HttpRequest

我如何自己复制这种行为?我已经尝试使用 Cats.Eval 作为示例,但我最终得到了相同的 Future[String]

另外,我在提出请求时面临一些嵌套问题,例如,要获取组织的贡献者列表,我需要创建两个 HttpRequest

  • 一个用于获取组织回购
  • 每个 repo 一个,以便获取贡献者列表

Future[List[Future[Users]]] 上的这个结果,和我面临同样的问题,为了获得结果,我必须这样做:

(result:Future[List[Future[Users]]]) onComplete { users =>
    users.foreach {
        _  onComplete {
            // Process result
        }
    }
}

但我想将值返回为github4s。我一直在阅读有关 Cats's Applicative and Traversable Functors 的文章,但没有运气。

【问题讨论】:

    标签: scala future scala-cats github4s


    【解决方案1】:

    您可以使用Future.sequenceList[Future] 转换为Future[List],然后压缩嵌套期货。

    生成的代码:

    val input: Future[List[Future[Users]]] = ???
    implicit  val ec: ExecutionContext = ExecutionContext.global
    val result: Future[List[Users]] = input.flatMap(list => Future.sequence(list))
    

    【讨论】:

    • 谢谢!等我回家试试。至于返回 Future 值而不是仅仅消耗它?
    • 不确定我是否完全理解您关于退货与消费的观点。此代码为您提供Future[List[Users]]。使用它的首选方法是对其调用map/flatMap 转换。 onComplete 通常不是最好的解决方案。
    • 但是如果我在Futuremap/flatMap 处理结果的唯一方法是在onComplete 上,那么github4s 如何将API 请求的结果放入变量中?
    • FutureGHIO 都表示可能失败或永远不会终止的计算。所以从Future[A] 中提取A 是一个不安全的操作,应该在可能的最新阶段进行。这是您提供的文档中的代码示例:
    • 不能告诉你很多关于Eval但它有.value函数来提取结果
    【解决方案2】:

    不知道为什么你认为这个问题会被猫解决。看来你到底想要的是String(正文值),而不是Future[String]

    从 Future 中提取值的唯一方法是使用 Await 阻止等待它的主线程。由于这不是很有效,您可以像以前一样使用onComplete 创建回调。

    需要注意的重要一点是,一旦您在Future 中包装了某些内容,大多数时候您都不想再将其取回。当你这样做时,它通常是在程序的边缘(例如你的主线程)。

    让我们来解决您的问题:val f = Future[List[Future[Users]]]。要取消嵌套,您需要:

    f.flatMap(Future.sequence) 给你Future[List[Users]]

    Future.sequence 会将Future[List[Future[Users]]] 变成Future[Future[List[Users]]]。它将列表中的所有期货合并为一个。任何失败的未来都会使外部未来成为失败。如果你不想这样,你可以.recover每个内部Future。

    f.flatMap将把Future[Future[List[Users]]变成Future[List[Users]]。它在外部 Future 完成后运行内部 Future。此处的错误处理与上述相同。

    【讨论】:

    • 谢谢,我想我对库如何使用 Cats.Eval 感到困惑,我有点认为他们是在使用猫来提取请求的正文。
    猜你喜欢
    • 2014-05-29
    • 2022-10-24
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    • 2017-09-09
    • 2014-09-13
    相关资源
    最近更新 更多