Raj Saxena Raj Saxena - 1 year ago 70
Scala Question

Returning value from Scala future completion

Coming from a Java background, I have been trying to teach myself Scala for some time now. As part of that, I am doing a small pet project that exposes a HTTP endpoint that saves the

registration number
of a vehicle against the
and returns the status.

To give more context, I am using Slick as FRM which performs DB operations asynchronously and returns a
Based on the output of this Future, I want to set the
variable to return back to the client.

Here, is the code

def addVehicleOwner(vehicle: Vehicle): String = {
var status = ""
val addFuture = db.run((vehicles returning vehicles.map(_.id)) += vehicle)
addFuture onComplete {
case Success(id) => {
BotLogger.info(LOGTAG, s"Vehicle registered at $id ")
status = String.format("Registration number - '%s' mapped to owner '%s' successfully", vehicle.registration,
println(s"status inside success $status") //--------- (1)
case Failure(e: SQLException) if e.getMessage.contains("SQLITE_CONSTRAINT") => {
status = updateVehicleOwner(vehicle)
BotLogger.info(LOGTAG, s"Updated owner='${vehicle.owner}' for '${vehicle.registration}'")
case Failure(e) => {
BotLogger.error(LOGTAG, e)
status = "Sorry, unable to add now!"
println(s"Status=$status") //--------- (2)

// Helper method for running a query in this example file:
def exec[T](sqlFuture: Future[T]):T = Await.result(sqlFuture, 1 seconds)

This was fairly simple in Java. With Scala, I am facing the following problems:

  • The expected value gets printed at (1), but (2) always prints empty string and same is what method returns. Can someone explain why?

  • I even tried marking the
    var status
    @volatile var status
    , it still evaluates to empty string.

  • I know, that the above is not the functional way of doing things as I am muting state. What is the clean way of writing code for such cases.

  • Almost all the examples I could find described how to map the result of
    or handle
    by doing a
    . I want to do more than that.

  • What are some good references of small projects that I can refer to? Specially, that follow TDD.

Answer Source

Instead of relying on status to complete inside the closure, you can recover over the Future[T] which handle the exception if they occur, and always returns the result you want. This is taking advantage of the nature of expressions in Scala:

val addFuture = 
  db.run((vehicles returning vehicles.map(_.id)) += vehicle)
    .recover {
      case e: SQLException if e.getMessage.contains("SQLITE_CONSTRAINT") => {
        val status = updateVehicleOwner(vehicle)
          s"Updated owner='${vehicle.owner}' for '${vehicle.registration}'"
      case e => {
        BotLogger.error(LOGTAG, e)
        val status = "Sorry, unable to add now!"

val result: String = exec(addFuture)
println(s"Status = $result")

Note that Await.result should not be used in any production environment as it synchronously blocks on the Future, which is exactly the opposite of what you actually want. If you're already using a Future to delegate work, you want it to complete asynchronously. I'm assuming your exec method was simply for testing purposes.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download