【问题标题】:Call 2 Futures in the same Action.async Scala Play在同一个 Action.async Scala Play 中调用 2 个期货
【发布时间】:2015-03-26 21:48:36
【问题描述】:

我是 Scala 的新手 :(也就是说,我正在与 Play 框架 Action.async 和 Future 调用作斗争。

我想在同一个 Action 中调用 2 个 Futures,然后等到它们都计算后在我的视图中发送它们的结果。

代码如下:

    def showPageWithTags(category: String) = Action.async {
        val page = PageDAO.findOne(Json.obj("category" -> category)).map {
          case Some(page) => {
            page.content = page.content.byteArrayToString
          }
        }
        val futureTags = ArticleDAO.listTags
        Ok(views.html.web.pages.show(page, futureTags))
   }

使用这个定义的函数:

def findOne(id: String): Future[Option[PageModel]]

def listTags: Future[List[String]] 

我收到此错误:

[error]  found   : Some[A]
[error]  required: models.PageModel
[error]         case Some(page) => {
[error]              ^
.../...
[error]  found   : None.type
[error]  required: models.PageModel
[error]         case None => {
[error]              ^
.../...
[error]  found   : Option[Nothing]
[error]  required: scala.concurrent.Future[play.api.mvc.Result]
[error]       optionPage.map {
[error]                      ^
.../...
[error]  found   : scala.concurrent.Future[Unit]
[error]  required: models.PageModel
[error]         Ok(views.html.web.pages.show(optionPage, futureTags))
[error]                                      ^
.../...
[error]  found   : scala.concurrent.Future[List[String]]
[error]  required: List[String]
[error]         Ok(views.html.web.pages.show(optionPage, futureTags))
[error]                                                  ^

我已经尝试使用 map、for/yield、foreach 来处理 Option 和 Future,但总是会出现这样的错误之一。

在我添加“标签”功能之前,一个 Future 调用运行良好:

  def showPage(category: String) = Action.async {
    PageDAO.findOne(Json.obj("category" -> category)).map {
      case Some(page) => {
        page.content = page.content.byteArrayToString
        Ok(views.html.web.pages.show(page))
      }
      case None => {
        NotFound
      }
    }
  }

如何在同一个操作中调用 2 个 Futures 并等到它们都计算通过 Ok() 将它们传递给我的页面视图?

非常感谢任何澄清!

【问题讨论】:

    标签: scala playframework future


    【解决方案1】:

    您需要map 和flatMap Futures 才能异步访问它们的结果。 Action.async 需要 Future[Result],因此您必须将您的 Futures 映射到它。这是一个简单的方法:

    def showPageWithTags(category: String) = Action.async {
        val page = PageDAO.findOne(Json.obj("category" -> category)).map {
          case Some(page) => {
            page.content.byteArrayToString
          }
        }
        val futureTags = ArticleDAO.listTags
    
        page.flatMap { page =>
            futureTags.map { tags =>
                Ok(views.html.web.pages.show(page, tags))
            }
        }
    }
    

    或者您可以使用 for-comprehension 来清理它,它是 flatMap/map 的语法糖。

    def showPageWithTags(category: String) = Action.async {
        for {
            pageOpt <- PageDAO.findOne(Json.obj("category" -> category))
                .map(_.map(_.content.byteArrayToString))
            tags <- ArticleDAO.listTags
        } yield {
            pageOpt.map { page =>
                Ok(views.html.web.pages.show(page, tags))
            } getOrElse {
                NotFound
            }
        }
    }
    

    我还冒昧地在 findOne 结果中简化了您的 map。

    【讨论】:

    • 感谢您的回答!你的第一个建议得到[error] found : Unit [error] required: models.PageModel [error] Ok(views.html.web.pages.show(page, tags)) [error] ^ 第二个[error] found : Option[String] [error] required: models.PageModel [error] } yield Ok(views.html.web.pages.show(page, tags)) [error] ^ 这就是我两天以来一直在反对的......
    • 我不能保证这会编译,因为我不知道视图的签名是什么。我没有看到任何会导致它推断出Unit 的东西,除非某些内容被错误地复制/粘贴。
    【解决方案2】:

    您可以使用Future 的flatMap 方法。

    flatMap 方法采用一个函数,该函数将值映射到新的未来 g,然后返回一个在 g 完成后完成的未来。

    (来自http://docs.scala-lang.org/overviews/core/futures.html)

    所以不妨试试:

    def showPage(category: String) = Action.async {
      ArticleDAO.listTags.flatMap { tags =>
        PageDAO.findOne(Json.obj("category" -> category)).map {
          case Some(page) => {
            page.content = page.content.byteArrayToString
            Ok(views.html.web.pages.show(page, tags))
          }
          case None => {
            NotFound
          }
        }
      }
    

    【讨论】:

    • #Tabo :就是这样! ArticleDAO.listTags 和 flatMap 应该首先被调用。非常感谢你们!
    • @jeanjerome 如果一个不依赖另一个,它们嵌套/调用的顺序无关紧要——它们不在这里。
    【解决方案3】:

    你可以这样试试。

    def showPageWithTags(category: String) = Action.async {
    
       for{
           pOpt <- PageDAO.findOne(Json.obj("category" -> category))
          futureTags <- ArticleDAO.listTags
          } yield{
              pOpt match {
               case Some(x)=> Ok(views.html.web.pages.show(x.content.byteArrayToString, futureTags))
               case _=> NotFound
                         }              
                }         
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-12
      • 2020-11-10
      • 2013-04-03
      • 2015-08-13
      相关资源
      最近更新 更多