user14190485
user14190485

Reputation:

Getting values from scala futures

I am new to scala and working on Futures. So I have a method call which returns a Future and my code is as follows.

val futureResult =  data.getState  //this returns future
    Await.result(id, 10 seconds)  //  waiting for future to complete
  futureResult .onComplete({
      case Success(result) => logger.info(s"resultis.. ${result}")
      case Failure(exception) => exception.printStackTrace()
    })

I am getting desired result in "result" in case Success. But I want to get value of result in a variable to pass it to some other method. I tried following.

val finalResult = futureResult.onComplete({
      case Success(result) => logger.info(s"resultis.. ${result}")
      case Failure(exception) => exception.printStackTrace()
    })

when I print finalResult, It is empty. How I can do have value of result So that it can be used at other places

Upvotes: 0

Views: 812

Answers (2)

ColOfAbRiX
ColOfAbRiX

Reputation: 1059

When you start working with Futures and similar objects collectively called "functors" or "monads", depending on their capabilities, (check out this guide on functors with amazing animated pictures) you are working with containers, boxes with special properties, and you don't want to "get the value" out of them unless strictly necessary. In the case of Future the container represent a value calculated asynchronously.

This means that you should be working with map,flatMap and similar methods to use the result of the computation. This way you can keep the whole computation asynchronous. As well, the other functions in your code should return Futures and should carry them along as much as you can. Here is the Scala docs overview on Future

At the same time if you assign a Future to a variable you're assigning the "Future container" to the variable, not the value that the Future will contain. That's why you can't print finalResult in your second example.

For instance, you can try:

def doSomething(value: String): Unit = ???
def calculateSomething(value: String): Boolean = ???
def calculateSomethingElse(value: Boolean): Future[Int] = ???

def myCode(...): Future[Int] = {
  someCallThatReturnsFutureOfString(...)
    .map { result =>
      logger.info(s"Result is... $result")
      // More computations with result
      doSomething(result)
      calculateSomething(result)
    }
    .flatMap { someCalculationResult =>
      // More computations....
      calculateSomethingElse(result)
    }
  }

If you want to deal with Exceptions, as I think you want to, this other answer is what you're looking for because transform allows you to manipulate the succesful result as well as the exception.

futureResult transform {
  case Success(result) =>
    println(s"Result is... $result")
    // More computations with result
    Success(calculateSomething(result))

  case f @ Failure(exception) =>
    exception.printStackTrace()
    f
}

I know, I sound like I'm not answering your question. You might be thinking "Hey, I want to get the value out and use it somewhere else, this guy is not telling me that" but unless specific situations that's not how they are meant to be used. Switching to this way of thinking requires effort and it's not easy to "see how it shold be done" at the beginning.

If you still want to get the value out, then Vincent's answer is a good one.

Upvotes: 1

Vincent
Vincent

Reputation: 46

Best is to do it this way :

val futureResult =  data.getState.map(res => fn(res))  //this returns future
  futureResult .onComplete({
      case Success(result) => logger.info(s"resultis.. ${result}")
      case Failure(exception) => exception.printStackTrace()
    })

Or at least :

val futureResult =  data.getState  //this returns future
  futureResult .onComplete({
      case Success(result) => 
           logger.info(s"resultis.. ${result}")
           fn(result)
      case Failure(exception) => exception.printStackTrace()
    })

But doing Await.result exposes you to the fact that an exception could be thrown in case the Future has failed, and make your program crash.

What is containg your expected "finalResult" is what's returned by Await.result

See method contract:

def result[T](awaitable : scala.concurrent.Awaitable[T], atMost : scala.concurrent.duration.Duration) : T

Upvotes: 0

Related Questions