scala – 播放中的多个期货,并使用案例类来保存未来数据

场景:

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

然后我需要采取这两个并构建一个FrontPage对象.在FrontPage对象的实例化期间,它创建一个Seq [NewProducts].每当它生成NewProduct时,NewProduct也必须在Future中的Web上调用MongoDB.在将FrontPage对象移交给视图之前,必须完成每个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]

    }
}

这是控制器到目前为止的代码:

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(以及设计者),并在其上定义一组非常简单的函数供他们使用.所有的期货都必须回归. Catalogue1和Catalogue2不依赖于任何东西甚至彼此.在FrontPage对象中创建Seq [NewProducts]取决于它们返回的两个.然后,我无法将FrontPage对象传递给视图,直到它从Mongo返回NewProducts.

这种复杂程度高于我习惯的程度.我对何时何地使用/ yield理解感到困惑.我担心这会以某种方式阻止因为期货太过嵌入案例类,在案例类中.控制器的最高级别包含在异步中,这是否意味着该异步调用中的任何和所有Futures都将是非阻塞的?

解决方法

将期货视为完全完整首页的步骤,而不是其中的一部分,并考虑这些步骤的每个小部分.

例如,要构造NewProduct的实例,请创建一个与db对话并返回未来已完成的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))

然后,您可以在处理加载/未来的函数/方法中再次创建首页:

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 in的集合和一个来自A =>的函数.未来[B]并返回Future [集合[B]].

然后,您可以在异步控制器中调用它以提供给模板:

def page() = Action.async { 
   for {
     frontPage <- loadFrontPage
   } yield Ok(views.some.template(frontPage))
 }

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...