scala - 多个 Futures in Play 和使用案例类来保存 future 数据

标签 scala playframework-2.0

场景:

我有两个不同的 Api 调用(通过网络)。 ApiCall1,ApiCall2。最终 ApiCall1 将返回一个 Option[Catalogue] 并且 ApiCall2 将返回一个 Seq[Catalogue]

然后我需要使用这两个并构建一个 FrontPage 对象。在 FrontPage 对象的实例化过程中,它创建了一个 Seq[NewProducts]。每次创建 NewProduct 时,NewProduct 还必须在 Future 中通过网络调用 MongoDB。在将 FrontPage 对象交给 View 之前,每个 Future 都必须已完成。

下面是 FrontPage 类的代码:

case class FrontPage(maybeCat1: Option[Catalogue], maybeCat2: Seq[Catalogue]) {

   val newProducts:Seq[NewProduct] = {
       maybeCat2.map( { cat =>
           NewProduct(cat)
       })
   } 
}

到目前为止,这是 NewProduct 类的代码:
case class NewProduct(cat:Catalogue) {
    val indivProduct:Option[IndivProduct] = {

        // ??? 
        // This next line goes out to Mongo and returns a Future[List[JsObject]]
        val indiv:Future[List[JsObject]] = MongoFetch.getIndivProduct(cat)

        //need to strip out the 'Future', wait for it to return?
        val listJS = indiv .. ???? // <-- need just the List[JsObject]]

        return IndivProduct(listJs)  // <-- constructs a new Option[IndivProduct]

    }
}

到目前为止,这是 Controller 的代码:
def landing() = Action.async {
   for {
      catalogue1 <- models.Granite.getCatalogue("front-page") // <- ApiCall1
      catalogue2 <- models.Granite.getCatalogue("tags")  // <- ApiCall2

   } yield {

      //??? How to now build the FrontPage object
      // surely it also depends on the future? 
      val fp = FrontPage(catalogue1, catalogue2)

      Ok(views.html.frontpage.landing(fp))  // <- at this point all futures must have returned.
   }
}

我真的希望能够将一个漂亮整洁的 FrontPage 对象传递给 View(以及设计者),并在其上定义一组非常简单的函数供他们使用模板。所有的 future 都必须返回。 Catalogue1 和 Catalogue2 不依赖任何东西,甚至彼此不依赖。在 FrontPage 对象内创建 Seq[NewProducts] 取决于它们都返回了。然后我不能将 FrontPage 对象传递给 View ,直到它从 Mongo 返回了 NewProducts。

这种复杂程度超出了我的习惯。我对何时何地使用 for/yield comprehensions 感到困惑。我担心这会以某种方式阻塞,因为 Futures 嵌入在案例类中太远了,在案例类中。 Controller 的最顶层被包裹在一个 Async 中,所以这是否意味着该 Async 调用中的任何和所有 Futures 都将是非阻塞的?

最佳答案

将 future 视为获得完整首页的步骤,而不是其中的一部分,并考虑这些步骤的每个小部分是什么。

例如,要构造 NewProduct 的实例,请创建一个与数据库对话并返回 future 完成的 NewProduct 实例的方法。

case class NewProduct(cat:Catalogue, indiv: Option[IndivProduct]) 

def newProductFor(cat: Catalogue): Future[NewProduct] = 
  for {
    listJs <- MongoFetch.getIndivProduct(cat)
  } yield NewProduct(cat, IndivProduct(listJs))

然后,您可以再次在处理加载/ future 的函数/方法中创建您的首页:
case class FrontPage(
  maybeCat1: Option[Catalogue], 
  maybeCat2: Seq[Catalogue], 
  newProducts: Seq[NewProduct]) 

def loadFrontPage: Future[FrontPage] = 
  for {
    catalogue1 <- models.Granite.getCatalogue("front-page")
    tags <- models.Granite.getCatalogue("tags")
    newProducts <- loadNewProducts(tags)
  } yield FrontPage(catalogue1, tags, newProducts)


def loadNewProducts(catalogues: Seq[Catalogue]): Future[Seq[NewProduct]] = {
  Future.traverse(catalogues) { catalogue => 
    newProductFor(catalogue) 
  }
}

注意 Future.traverse 接受一个包含 A:s 的集合和一个来自 A => Future[B] 并返回一个 Future[collection[B]] 的函数。

然后,您可以在异步 Controller 中调用它以提供给模板:
 def page() = Action.async { 
   for {
     frontPage <- loadFrontPage
   } yield Ok(views.some.template(frontPage))
 }

关于scala - 多个 Futures in Play 和使用案例类来保存 future 数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22478103/

相关文章:

带有 CDN 的 Java Play2

scala - 如何从 Play 中的请求中获取 InputStream

scala - akka Actor 向邮箱负责人发送消息

sql - 以编程方式向 Spark DataFrame 添加多个列

scala - 使用 shapeless-scalacheck 派生任意函数实例

scala - 将列表转换为案例类

scala - 是否可以/建议使用elastic4s为JSON数据的字符串编制索引?

scala - 从 Typesafe 安装的 Play 2.0 中缺少 'debug' 命令

java - 如何使用辅助键上的联接建立一对一关系?

java - 玩! 2.1-RC2 没有静态引用的 JavaForms validate()