如何摆脱scala中的嵌套未来?

jbr*_*own 1 scala playframework

我的播放框架应用程序中有一些代码可以解析 JSON 请求并使用它来更新用户的数据。问题是我需要返回 a Future[Result],但我的userDAO.update函数返回 aFuture[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)
}
}
Run Code Online (Sandbox Code Playgroud)

更新

草皮定律:在发布这篇文章后,我解决了:

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))
}
Run Code Online (Sandbox Code Playgroud)

但是,有没有更优雅的方式呢?似乎我在Future()四处闲逛,这似乎是一种代码气味。

pam*_*amu 5

使用flatMap代替map

flatMap[A, B](f: A => Future[B])

map[A, B](f: A => B)
Run Code Online (Sandbox Code Playgroud)

更优雅的方式是使用 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
Run Code Online (Sandbox Code Playgroud)