如何包装 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 user
和 avatar
的 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]]
,请使用 flatMap
。 yield
被脱糖为带有签名 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)
}
}
我有一个处理表单提交的操作。在验证表单之前,我需要解析两个 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 user
和 avatar
的 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]]
,请使用 flatMap
。 yield
被脱糖为带有签名 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)
}
}