0

In my playframework application I want to wait until my future is completed and the return it to the view.

my code looks like:

 def getContentComponentUsageSearch: Action[AnyContent] = Action.async { implicit request =>
    println(request.body.asJson)
    request.body.asJson.map(_.validate[StepIds] match {
      case JsSuccess(stepIds, _) =>

        println("VALIDE SUCCESS -------------------------------")


        val fList: List[Seq[Future[ProcessTemplatesModel]]] = List() :+ stepIds.s.map(s => {
          processTemplateDTO.getProcessStepTemplate(s.processStep_id).flatMap(stepTemplate => {
            processTemplateDTO.getProcessTemplate(stepTemplate.get.processTemplate_id.get).map(a => {
              a.get
            })
          })
        })


        fList.map(u => {
          val a: Seq[Future[ProcessTemplatesModel]] = u

          Future.sequence(a).map(s => {
            println(s)
          })
        })


        Future.successful(Ok(Json.obj("id" -> "")))

      case JsError(_) =>
        println("NOT VALID -------------------------------")
        Future.successful(BadRequest("Process Template not create client"))
      case _ => Future.successful(BadRequest("Process Template create client"))
    }).getOrElse(Future.successful(BadRequest("Process Template create client")))
  }

the pirntln(s) is printing the finished stuff. But how can I wait until it is complete and return it then to the view?

thanks in advance

UPDATE:

also tried this:

  val process = for {

      fList: List[Seq[Future[ProcessTemplatesModel]]] <- List() :+ stepIds.s.map(s => {
        processTemplateDTO.getProcessStepTemplate(s.processStep_id).flatMap(stepTemplate => {
          processTemplateDTO.getProcessTemplate(stepTemplate.get.processTemplate_id.get).map(a => {
            a.get
          })
        })
      })

    } yield (fList)





    process.map({ case (fList) =>
      Ok(Json.obj(
        "processTemplate" -> fList
      ))
    })

but then I got this:

enter image description here

UPDATE: My problem is that the futures in fList do not complete before an OK result is returned

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
Felix
  • 5,452
  • 12
  • 68
  • 163
  • Not exactly duplicate of [Scala Future not printing results](https://stackoverflow.com/questions/49694113/scala-future-not-printing-results), because there, there was no problem with piping the result to a Play view asynchronously... – Andrey Tyukin Apr 06 '18 at 15:16
  • Await is not a acceptable solution. isn't there another way? – Felix Apr 06 '18 at 15:17
  • Again, the same problem as with the other question. You write "I want ", then you dump a gigantic wall of non-compilable code with thousand undefined types and variables, and expect to get a meaningful answer. Please reduce it to an [MCVE](https://stackoverflow.com/help/mcve) and state clearly what you tried and what the exact problem is. Eliminate the unnecessary details. Piping a single "hello"-String from a future to the view would be an example of essentially the same problem, but with none of the dependencies and undefined variables. – Andrey Tyukin Apr 06 '18 at 15:22
  • the problem is just to wait until a future is complete. A minimal example is not possible. because the part I postet is only a very very small part.... – Felix Apr 06 '18 at 15:23
  • No, you don't want to wait until the future is complete, because Play is built on top of Akka, and in Akka everything can be done asynchronously, without blocking the thread. Have you just confused this comment thread with the linked comment thread, where there was neither Play no Akka, or what? – Andrey Tyukin Apr 06 '18 at 15:28
  • what? ... now we are ablute broad .... I want to wait! I know how to wait without blocking when I do a database request. But in this Case I add items to a list. and there the problem is.... This list is not build completely bevore OK is fired. – Felix Apr 06 '18 at 15:31
  • What I meant by "you don't want to wait" is (quote from [Play documentation](https://www.playframework.com/documentation/2.6.x/ScalaAsync) : *"The default configuration is tuned for asynchronous controllers. In other words, the application code should avoid blocking in controllers"*. So, as you said, `Await` is not acceptable - I understood this under "to wait". So, your problem is that the futures in `fList` do not complete before an `OK` result is returned, is that your problem? Then please consider updating your question to point out this exact problem more explicitly. – Andrey Tyukin Apr 06 '18 at 15:47
  • yes thats exactly my problem! do you have any hints for me? – Felix Apr 06 '18 at 15:48
  • @Felix You don't want to wait until the future is completed. You want to return an async action in play. Read about returning futures in play controllers. Also yeah, start with a small example, like a method returning Future.successful("something") and go from there until you figure out what is causing the problem. – nmat Apr 06 '18 at 16:01
  • @nmat small examples are working;) thats not the problem can you maybe give some more hints? or can you go deeper into my issue? – Felix Apr 06 '18 at 16:03

2 Answers2

4

The code in the question didn't seem compilable, so here is an untested very rough sketch, that hopefully provides enough inspiration for further search of the correct solution:

def getContentComponentUsageSearch: = Action.async { implicit req =>
  req.body.asJson.map(_.validate[StepIds] match {
    case JsSuccess(stepIds, _) => {

      // Create list of futures
      val listFuts: List[Future[ProcessTemplatesModel]] = (stepIds.s.map(s => {
        processTemplateDTO.
          getProcessStepTemplate(s.processStep_id).
          flatMap{ stepTemplate => 
            processTemplateDTO.
              getProcessTemplate(stepTemplate.get.processTemplate_id.get).
              map(_.get)
          }
      })).toList

      // Sequence all the futures into a single future of list
      val futList = Future.sequence(listFuts)

      // Flat map this single future to the OK result
      for {
        listPTMs <- futList
      } yield {
        // Apparently some debug output? 
        listPTMs foreach printl

        Ok(Json.obj("id" -> ""))
      }
    }

    case JsError(_) => {
      println("NOT VALID -------------------------------")
      Future.successful(BadRequest("Process Template not create client"))
    }

    case _ => Future.successful(BadRequest("Process Template create client"))

  }).getOrElse(Future.successful(BadRequest("Process Template create client")))
}

If I understood your question correctly, what you wanted was to make sure that all futures in the list complete before you return the OK. Therefore I have first created a List[Future[...]]:

  val listFuts: List[Future[ProcessTemplatesModel]] = // ...

Then I've combined all the futures into a single future of list, which completes only when every element has completed:

  // Sequence all the futures into a single future of list
  val futList = Future.sequence(listFuts)

Then I've used a for-comprehension to make sure that the listPTMs finishes computation before the OK is returned:

  // Flat map this single future to the OK result
  for {
    listPTMs <- futList
  } yield {
    // Apparently some debug output? 
    listPTMs foreach printl

    Ok(Json.obj("id" -> ""))
  }

The for-yield (equivalent to map here) is what establishes the finish-this-before-doing-that behavior, so that listPTMs is fully evaluated before OK is constructed.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
2

In order to wait until a Future is complete, it is most common to do one of two things:

Use a for-comprehension, which does a bunch of mapping and flatmapping behind the scenes before doing anything in the yield section (see Andrey's comment for a more detailed explanation). A simplified example:

def index: Action[AnyContent] = Action.async {    
  val future1 = Future(1)
  val future2 = Future(2)

  for {
    f1 <- future1
    f2 <- future2
  } yield {
    println(s"$f1 + $f2 = ${f1 + f2}") // prints 3
    Ok(views.html.index("Home"))
  }
}

Map inside a Future:

def index: Action[AnyContent] = Action.async {    
  val future1 = Future(1)

  future1.map{
    f1 =>
    println(s"$f1")
    Ok(views.html.index("Home"))
  }
}

If there are multiple Futures:

def index: Action[AnyContent] = Action.async {

  val future1 = Future(1)
  val future2 = Future(2)

  future1.flatMap{
    f1 =>
      future2.map {
        f2 =>
          println(s"$f1 + $f2 = ${f1 + f2}")
          Ok(views.html.index("Home"))
      }
    }
  }
}

When you have multiple Futures though, the argument for for-yield comprehensions gets much stronger as it gets easier to read. Also, you are probably aware but if you work with futures you may need to following imports:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
James Whiteley
  • 3,363
  • 1
  • 19
  • 46