I'm a newby in Scala :( That's said, I'm fighting against Play framework Action.async and Future calls.
I'd like to call 2 Futures in the same Action and wait until they both compute to send their result in my view.
Here is the code :
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))
}
with this defined functions :
def findOne(id: String): Future[Option[PageModel]]
def listTags: Future[List[String]]
I get this errors :
[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] ^
I've tried map, for/yield, foreach to deal with Option and Future but there is always one of this errors which remains.
Yet the one Future call was working good before I add the "Tag" functionality :
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
}
}
}
How can I call 2 Futures in the same action and wait until they both compute to pass them to my page view via Ok() ?
Many thanks to any clarifications !
You need to map
and flatMap
the Future
s in order to access their results asynchronously. Action.async
expects a Future[Result]
, so you must map your Future
s to that. Here's a simple way:
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))
}
}
}
Or you can clean this up using a for-comprehension, which is syntactic sugar for 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
}
}
}
I also took the liberty of simplifying your map
on the findOne
results.
You can use the flatMap
method of the Future
.
The
flatMap
method takes a function that maps the value to a new futureg
, and then returns a future which is completed onceg
is completed.
(from http://docs.scala-lang.org/overviews/core/futures.html )
So maybe try:
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
}
}
}
You can try like this.
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
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.