gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 02:18:36 28 4
gpt4 key购买 nike

场景:

我有两个不同的 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/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com