我的play框架应用程序中有一些代码可以解析JSON请求并使用它来更新用户的数据。问题是我需要返回Future[Result]
,但我的userDAO.update
函数返回Future[Int]
,所以我有嵌套的期货。
我使用的Await
并不是很好。如何重写此代码以避免嵌套的未来?
def patchCurrentUser() = Action.async { request =>
Future {
request.body.asJson
}.map {
case Some(rawJson) => Json.fromJson[User](rawJson).map { newUser =>
val currentUserId = 1
logger.info(s"Retrieving users own profile for user ID $currentUserId")
val futureResult: Future[Result] = userDAO.findById(currentUserId).flatMap {
case Some(currentUser) =>
val mergedUser = currentUser.copy(
firstName = newUser.firstName // ... and the other fields
)
userDAO.update(mergedUser).map(_ => Ok("OK"))
case _ => Future { Status(404) }
}
import scala.concurrent.duration._
// this is bad. How can I get rid of this?
Await.result(futureResult, 1 seconds)
}.getOrElse(Status(400))
case _ => Status(400)
}
}
更新:
索德定律:在发布之后我就把它解决了:
Future {
request.body.asJson
}.flatMap {
case Some(rawJson) => Json.fromJson[User](rawJson).map { newUser =>
val currentUserId = 1
userDAO.findById(currentUserId).flatMap {
case Some(currentUser) =>
val updatedUser = currentUser.copy(
firstName = newUser.firstName
)
userDAO.update(updatedUser).map(_ => Ok("OK"))
case _ => Future { Status(404) }
}
}.getOrElse(Future(Status(400)))
case _ => Future(Status(400))
}
但是,有更优雅的方式吗?好像我在quite Future()
周围充满了气味,这似乎是一种代码味道。
答案 0 :(得分:4)
使用flatMap
代替map
。
flatMap[A, B](f: A => Future[B])
map[A, B](f: A => B)
更优雅的方式是使用for comprehension
使用For comprehension代码如下所示
for {
jsonOpt <- Future (request.body.asJson)
result <- jsonOpt match {
case Some(json) =>
json.validate[User] match {
case JsSuccess(newUser, _ ) =>
for {
currentUser <- userDAO.findById(1)
_ <- userDAO.update(currentUser.copy(firstName = newUser.firstName))
} yield Ok("ok")
case JsError(_) => Future(Status(400))
}
case None => Future(Status(400))
}
} yield result
答案 1 :(得分:2)
正如@pamu所说,如果您使用for comprehension,它可能会清除您的代码。
另一种有趣的方法(在功能编程方面更纯粹)是使用monad transformers
(通常类似于Future[Option[T]]
类型的monad变换器)。
你应该看看像cats(和/或scalaz)这样的图书馆。我试着给出一个小的伪代码&#34;使用猫的例子(因为我没有在本地玩游戏框架):
import cats.data.OptionT
import cats.instances.future._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
def convertJsonToUser(json: Json): Future[Option[User]] = Json.fromJson[User](json)
def convertBodyToJson(request: Request): Future[Option[Json]] = Future {request.body.asJson}
def updateUser(user: User): Future[HttpResult] = Future {
// update user
Ok("ok")
}
def myFunction: Future[HttpResult] = {
val resultOpt: OptionT[Future, HttpResult] = for {
json <- OptionT(convertBodyToJson(request))
user <- OptionT(convertJsonToUser(json))
result <- OptionT.lift(updateUser(user))
} yield result
result.getOrElseF(Future {Status(400)})
}
正如您所看到的,在这种情况下,monad变换器允许将类似Future[Option[T]]
的类型视为单个&#34;短路&#34;类型(例如,如果你有一个失败的未来,或者包含一个无的未来,那么理解就会停止。)