使用 Cats 从 Future 中提取价值

Extract value from Future using Cats

我正在尝试为 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
}

我正在引用文档:

Every Github4s API call returns a GHIO[GHResponse[A]] which is an alias for Free[Github4s, GHResponse[A]].

GHResponse[A] is, in turn, a type alias for Either[GHException, GHResult[A]].

GHResult contains the result A given by Github as well as the status code and headers of the response:

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

我怎样才能自己复制这种行为?我试过在示例中使用 Cats.Eval,但我最终得到了相同的 Future[String].

此外,我在提出请求时遇到了一些嵌套问题,例如,要获取组织的贡献者列表,我需要制作两个 HttpRequest:

这个结果就Future[List[Future[Users]]],和我遇到和上面一样的问题,为了得到结果,我必须做:

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

不过我想把return的值设为github4s。我一直在阅读有关 Cats's Applicative and Traversable Functors 的文章,但运气不佳。

您可以使用 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))

不确定你为什么认为这个问题会被猫解决。看来你最终要的是String(body value),而不是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]]]。它将列表中的所有 Futures 合并为一个。任何失败的未来都会使外在的未来失败。如果你不想这样,你可以 .recover 每个内部 Future。

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