VBAHole
VBAHole

Reputation: 1518

Evaluating a Future Boolean asynchronously using Scala and Play

I have a method that returns a Future[Boolean] in a Play controller and i want to evaluate that using async but i can't seem to get it to compile. The following will work:

  def health = Action {
    logger.info("Endpoint method: health")
    val isHealthy = healthCheckService.checkDynamo()
    val b: Boolean = Await.result(isHealthy, scala.concurrent.duration.Duration(5, "seconds"))
    Ok(Json.toJson(HealthCheckResponse(b.toString)))
  }

But i don't think i want that Await in there. So i'm trying things like this with no success:

 def health =
    Action.async {
      Future {
        logger.info("Endpoint method: health")


        healthCheckService.checkDynamo() match {
          case Future.successful(true)  => Ok(Json.toJson("false"))
          case false => Ok(Json.toJson("true"))
        }



        val r = healthCheckService.checkDynamo() match {
          case true  => Ok(Json.toJson("false"))
          case false => Ok(Json.toJson("true"))
        }

      }
    }

I can't even get those to compile to test them out. Any suggestions?

Upvotes: 0

Views: 804

Answers (2)

Lasf
Lasf

Reputation: 2582

Try this:

def health = Action.async {
  healthCheckService.checkDynamo().map {
    case true => Ok(Json.toJson("false"))
    case false => Ok(Json.toJson("true"))
  }
}

Let Play handle the awaiting for you under the hood. That is, Action.async accepts a Future, which checkDynamo() already returns. All you have to do is map it to the appropriate result.

Upvotes: 2

Vladimir Matveev
Vladimir Matveev

Reputation: 128111

With Futures you have to use combinators like map and flatMap to express the final value. For example:

Action.async {
  healthCheckService.checkDynamo()
    .map { result =>  // boolean
      HealthCheckResponse(result.toString)
    }
    .map(Json.toJson(_))
    .map(Ok(_))
}

(You can merge maps above to one map and construct the final Ok value there; it is more or less a matter of taste)

If you have, say, two async calls which you want to execute and return a result based on their results, you can use flatMap, which could be easily expressed using a for comprehension:

Action.async {
  for {
    result1 <- someService.someCall()
    result2 <- anotherService.anotherCall(result1.someProperty)
    finalResult = SomeFinalResultType(result1, result2)
  } yield Ok(Json.toJson(finalResult))
}

If you are not familiar with futures, you might want to read some tutorial which explains their nature, how to combine them and how to get useful results from them, like this one: http://hello-scala.com/920-scala-futures.html

Upvotes: 2

Related Questions