简体   繁体   中英

Call 2 Futures in the same Action.async Scala Play

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 future g , and then returns a future which is completed once g 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM