【问题标题】:How to wrap Scala for comprehension to resolve Future around Play's Form folding?如何包装 Scala 以便理解以解决 Play 的 Form 折叠的 Future?
【发布时间】:2016-09-17 06:56:42
【问题描述】:

我有一个处理表单提交的操作。在验证表单之前,我需要解析两个 Futures。我以为我可以嵌套所有内容,这意味着将 fold 放在 for 理解的 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))
    }
  }

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

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

【问题讨论】:

    标签: scala playframework akka playframework-2.2


    【解决方案1】:

    如果我正确理解了您的问题,这就是解决问题的方法:

      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
      }
    

    简单地说,不要这么快屈服。如果你是为了理解而写作,而某些东西正在返回未来,请使用 &lt;- 提取值,然后生成它。

    &lt;- 被翻译成flatMap 并带有签名(简体)flatMap(f: A =&gt; Future[B]): Future[B]。因此,如果您的函数返回一个未来并且您不想嵌套Future[Future[A]],请使用flatMapyieldmap 脱糖并带有签名 map(f: A =&gt; B): Future[B] 因此这是f 不返回 Future 的情况,除非您出于某种原因希望它嵌套。如果用于折叠表单绑定结果的两个分支都返回Future[A],那么您想使用flatMap

    附带说明,您不是同时获取用户和头像,而是一个接一个。您可能希望先开始计算,然后“等待”两者都完成。这可以通过多种方式完成,例如:

    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
    

    【讨论】:

      【解决方案2】:

      Zip 和平面地图

      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)
      
          }
      
        }
      

      【讨论】:

        猜你喜欢
        • 2015-10-09
        • 2018-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-27
        • 2022-01-20
        • 2016-01-02
        相关资源
        最近更新 更多