Извлечение ценности из будущего с использованием кошек - PullRequest
2 голосов
/ 29 октября 2019

Я пытаюсь написать небольшую библиотеку для API GitHub. И я пытаюсь смоделировать поведение библиотеки Github4s , вот пример этой библиотеки:

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
}

Я цитирую документы:

Каждый вызов API Github4s возвращает GHIO [GHResponse [A]], который является псевдонимом для Free [Github4s, GHResponse [A]].

GHResponse [A], в свою очередь, является псевдонимом типа для Either [GHException, GHResult [A]].

GHResult содержит результат, данный Github, а также код состояния и заголовки ответа:

В какой-то момент они делаютHttpRequest с использованием HttpClient.scala

Как я мог сам повторить это поведение? Я попытался использовать Cats.Eval, как в примере, но в итоге получился тот же Future[String].

Кроме того, у меня возникают некоторые проблемы с вложением, когда я делаю запрос, например, чтобы получить список участников организации, мне нужно сделать два HttpRequest:

  • Один для получения репо организации
  • Один для каждого репо, чтобы получить список участников

Этот результат на Future[List[Future[Users]]], и я сталкиваюсь с той же проблемой, что и выше,чтобы получить результаты, мне нужно сделать:

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

Но я бы хотел вернуть значение, как github4s. Я читал про Аппликативные и проходимые функторы кошек без удачи.

Ответы [ 2 ]

2 голосов
/ 29 октября 2019

Не уверен, почему вы думаете, что эту проблему решат кошки. Похоже, что в итоге вы хотите получить 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 каждое внутреннее Будущее.

f.flatMap превратит Future[Future[List[Users]] в Future[List[Users]]. Это управляет внутренним Будущим после того, как внешнее заканчивается. То же самое для обработки ошибок, как указано выше.

2 голосов
/ 29 октября 2019

Вы можете конвертировать List[Future] в Future[List], используя Future.sequence, а затем раздавить вложенные фьючерсы.

Полученный код:

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