Await.result на HttpService
У меня есть проект Scala с http4s 0.15.16a и Slick 3.2.1 с этими шагами:
- Получить удостоверение по звонку отдыха
- передача идентификатора в MySlickDAO, который отвечает Future
- Вызов Await.result(res, Duration.Inf) в будущем, возвращаемом MySlickDAO
- Создать JSON
Проблема в том, что я использую Await.result, и это плохая практика, есть ли лучшее решение?
Вот код:
val service = HttpService {
//http://localhost:8080/rest/id/9008E75A-F112-396B-E050-A8C08D26075F
case GET -> Root / "rest" / "id" / id =>
val res = MySlickDAO.load(id)
Await.result(res, Duration.Inf)
val ll = res.value.get.get
ll match {
case Failure(x) =>
InternalServerError(x)
case Success(record) =>
val r = record.map(x => MyEntity(x._1, x._2, x._3))
jsonOK(r.asJson)
}
case ....
}
2 ответа
Вместо ожидания вы можете связать результат одного Future
в другое:
val resFut = MySlickDAO.load(id)
resFut.map { record =>
val r = record.map(x => MyEntity(x._1, x._2, x._3))
jsonOK(r.asJson)
} recover { x =>
InternalServerError(x)
}
Результатом этого будет Future
общего супертипа jsonOK
а также InternalServerError
(не знаком с библиотеками, которые вы используете; возможно, у меня неправильный тип загрузки: это не Future[Try[_]]
это?).
Кстати, ваш оригинальный код имеет очень проблемную строку:
val ll = res.value.get.get
res.value
является Option[Try[T]]
, призвание get
на Option
или Try
как правило, плохая идея (хотя в этом случае из-за Await
, Option
никогда не должно быть None
, Итак get
является технически безопасным), потому что это может вызвать исключение. Вы намного лучше использовать map
, flatMap
, и друзья.
Проблема заключается в том, что http4s 0.15 использует конструкции параллелизма Scalaz, в то время как Slick использует нативные конструкции Scala, и эти два не предназначены для работы друг с другом. Насколько я понимаю, http4s 0.17+ переключился с Scalaz на Cats, что может повлечь за собой использование нативного Scala Futures, так что если вы можете обновить его, возможно, стоит попробовать. Если нет, вы можете обработать преобразование, вручную создав задачу, которая обернет ваше будущее:
def scalaFutureRes = MySlickDAO.load(id)
val scalazTaskRes = Task.async { register =>
scalaFutureRes.onComplete {
case Success(success) => register(success.right)
case Failure(ex) => register(ex.left)
}
}
На данный момент у вас есть задача [ResultType] из будущего [ResultType], которую вы можете отобразить / flatMap с остальной частью вашей логики, как в ответе Леви.
Для этого вы также можете использовать библиотеку delorean, в которой эта логика и противоположное направление определены для рассматриваемых классов посредством неявных преобразований, так что вы можете просто вызвать.toTask для Future, чтобы получить его в совместимой форме. В их файле readme также содержится много полезной информации о конверсии и о возможных подводных камнях.