如何包装 Scala 以理解围绕 Play 的 Form 折叠解决 Future?

How to wrap Scala for comprehension to resolve Future around Play's Form folding?

我有一个处理表单提交的操作。在验证表单之前,我需要解析两个 Futures。我以为我可以嵌套所有内容,意思是将 fold 放在 for comprehension 的 yield 块中。

例如:

  def handleFormSubmission = silhouette.SecuredAction.async { implicit request =>
    for {
      user <- userService.findOneByUserId(userId)
      avatar <- avatarService.findOneByUserId(userId)
    } yield {
      myForm.bindFromRequest.fold(
        formWithErrors => formWithErrorsBranch(formWithErrors, user, avatar),
        changeData => changeDataBranch(changeData, user, avatar))
    }
  }

两个分支returnFuture[Result]fold的签名是def fold[R](hasErrors: Form[T] => R, success: T => R): R。据我了解,fold 接受两个函数,参数为 Form[T]T 以及 return R。这意味着如果我在两个分支 Future[Result] 中 return,fold 也将 return Future[Result]。但是,由于它包含在用于解析 Futures useravatar 的 for comprehension 中,因此我不需要 Future[Result] 而是 Result。那是对的吗?如果是这样,如何以非阻塞方式修复以下编译错误?

type mismatch;
 found   : scala.concurrent.Future[play.api.mvc.Result]
 required: play.api.mvc.Result

如果我对你的问题理解正确,可以这样解决:

  def handleFormSubmission = silhouette.SecuredAction.async { implicit request =>
    for {
      user <- userService.findOneByUserId(userId)
      avatar <- avatarService.findOneByUserId(userId)
      response <- myForm.bindFromRequest.fold(
        formWithErrors => formWithErrorsBranch(formWithErrors, user, avatar),
        changeData => changeDataBranch(changeData, user, avatar)
      )
    } yield response
  }

干脆,别这么快屈服。如果您是为了理解而写作,并且某些东西是 return 未来,请使用 <- 提取价值,然后产生它。

<- 翻译成 flatMap 签名(简体)flatMap(f: A => Future[B]): Future[B]。因此,如果您的函数 return 是 future 并且您不想嵌套 Future[Future[A]],请使用 flatMapyield 被脱糖为带有签名 map(f: A => B): Future[B]map 所以这是针对 f 不 return 和 Future 的情况,除非你想嵌套它因为某些原因。如果你的两个折叠表单绑定结果的分支 return a Future[A] 那么你想使用 flatMap.

顺便说一句,您不是同时获取用户和头像,而是一个接一个。您可能希望先开始计算,然后 "wait" 完成两者。这可以通过多种方式完成,例如:

val userFuture = userService.findOneByUserId(userId)
val avatarFuture = avatarService.findOneByUserId(userId)
for {
  user <- userFuture
  avatar <- avatarFuture
  response <- ...
} yield response

或者例如 zip

for {
  (user, avatar) <- userService.findOneByUserId(userId) zip avatarService.findOneByUserId(userId)
  response <- ...
} yield response

Zip 和 flatMap

zip 得到结果 future 然后 flatMap 创建结果。

recover 以防压缩的未来失败。

def handleFormSubmission = silhouette.SecuredAction.async { implicit request =>

    val userFuture = userService.findOneByUserId(userId)
    val avatarFuture = avatarService.findOneByUserId(userId)

    userFuture.zip(avatarFuture).flatMap { case (user, avatar) =>

      myForm.bindFromRequest.fold(
        formWithErrors => formWithErrorsBranch(formWithErrors, user, avatar),
        changeData => changeDataBranch(changeData, user, avatar))

    }.recover { case th =>

      Ok("error occurred because: " + th.getMessage)

    }

  }