Accessing value returned by scala futures
Asked Answered
K

6

57

I am a newbie to scala futures and I have a doubt regarding the return value of scala futures.

So, generally syntax for a scala future is

 def downloadPage(url: URL) = Future[List[Int]] {

 }

I want to know how to access the List[Int] from some other method which calls this method.

In other words,

val result = downloadPage("localhost") 

then what should be the approach to get List[Int] out of the future ?

I have tried using map method but not able to do this successfully.`

Kelly answered 18/7, 2013 at 2:44 Comment(0)
B
30

The case of Success(listInt) => I want to return the listInt and I am not able to figure out how to do that.

The best practice is that you don't return the value. Instead you just pass the future (or a version transformed with map, flatMap, etc.) to everyone who needs this value and they can add their own onComplete.

If you really need to return it (e.g. when implementing a legacy method), then the only thing you can do is to block (e.g. with Await.result) and you need to decide how long to await.

Brigette answered 18/7, 2013 at 6:5 Comment(10)
So there is no way you can return the value of a Future without blocking? I thought that scala's goal was to be a more functional capable language. If Future callbacks return Unit, doesn't that support side-effects? Is there no way to get around this?Valrievalry
"So there is no way you can return the value of a Future without blocking?" How could you? The value isn't available yet. "If Future callbacks return Unit, doesn't that support side-effects? Is there no way to get around this?" Use map, flatMap, etc.Brigette
So my particular situation is checking to see if a user session is valid. In doing so, I make a request to a server to see if the session is valid. I use the Play Framework WS library which uses Futures. I just want to be able to return true or false from the isValidSession method without having to block the thread.Valrievalry
In this case "without having to block the thread" means "without having to wait for the server reply". How could this be possible? If you don't want to block the thread, your method should return Future[Boolean] instead.Brigette
@AlexeyRomanov It is possible, but you need a library like quasar which can do synchronous calls without thread blocking :)Laidlaw
@Laidlaw It is quite unnecessary information for this level of explanationChoate
@AlexeyRomanov Can you exactly give a sample code for this example if I have to get String from "Future[String]" using map, flatmap. I'm unable to get it using map/flatmap.Hendricks
@Hendricks You can't, just look at the return type. The point of the answer is that you generally don't need to. In the rare case in which it's necessary, you use Await.result.Brigette
@AlexeyRomanov I guess Await.result is also blocking. I guess using for comprehension and yield works to get the value out of Future.Hendricks
@Hendricks No, they don't. The type of for-comprehension where you yield a String will be Future[String].Brigette
R
17

You need to wait for the future to complete to get the result given some timespan, here's something that would work:

  import scala.concurrent.duration._

  def downloadPage(url: URL) = Future[List[Int]] {
    List(1,2,3)
  }

  val result = downloadPage("localhost")

  val myListInt = result.result(10 seconds)

Ideally, if you're using a Future, you don't want to block the executing thread, so you would move your logic that deals with the result of your Future into the onComplete method, something like this:

  result.onComplete({
    case Success(listInt) => {
      //Do something with my list
    }
    case Failure(exception) => {
      //Do something with my error
    }
  })
Rae answered 18/7, 2013 at 2:51 Comment(4)
My problem is result.onComplete({ case Success(listInt) => { //Do something with my list } case Failure(exception) => { //Do something with my error } }) will be a part of some method and I want to return the listInt value. How to do that ?Kelly
Either return future itself, or block until it completes.Toole
You want the first version I suggested val myListInt = result.result(10 seconds) though watch out for timeout exceptions...Rae
In Scala 2.10, you should use Await instead of getting the future's result directly: Await.result(result, 10 seconds)Rags
Y
8

I hope you already solved this since it was asked in 2013 but maybe my answer can help someone else:

If you are using Play Framework, it support async Actions (actually all Actions are async inside). An easy way to create an async Action is using Action.async(). You need to provide a Future[Result]to this function.

Now you can just make transformations from your Future[List[Int]] to Future[Result] using Scala's map, flatMap, for-comprehension or async/await. Here an example from Play Framework documentation.

import play.api.libs.concurrent.Execution.Implicits.defaultContext

def index = Action.async {
  val futureInt = scala.concurrent.Future { intensiveComputation() }
  futureInt.map(i => Ok("Got result: " + i))
}
Yajairayajurveda answered 9/5, 2015 at 21:20 Comment(0)
B
8

You can do something like that. If The wait time that is given in Await.result method is less than it takes the awaitable to execute, you will have a TimeoutException, and you need to handle the error (or any other error).

import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.util.{Try, Success, Failure}
import scala.concurrent.duration._

object MyObject {
    def main(args: Array[String]) {

        val myVal: Future[String] = Future { silly() }

        // values less than 5 seconds will go to 
        // Failure case, because silly() will not be done yet
        Try(Await.result(myVal, 10 seconds)) match {
            case Success(extractedVal) => { println("Success Happened: " + extractedVal) }
            case Failure(_) => { println("Failure Happened") }
            case _ => { println("Very Strange") }
        }      
    }

    def silly(): String = {
        Thread.sleep(5000)
        "Hello from silly"
        }
}
Bookerbookie answered 10/3, 2017 at 2:15 Comment(0)
P
3

The best way I’ve found to think of a Future is a box that will, at some point, contain the thing that you want. The key thing with a Future is that you never open the box. Trying to force open the box will lead you to blocking and grief. Instead, you put the Future in another, larger box, typically using the map method.

Here’s an example of a Future that contains a String. When the Future completes, then Console.println is called:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

object Main {

  def main(args:Array[String]) : Unit = {
    val stringFuture: Future[String] = Future.successful("hello world!")
    stringFuture.map {
      someString =>
        // if you use .foreach you avoid creating an extra Future, but we are proving
        // the concept here...
        Console.println(someString)
    }
  }
}

Note that in this case, we’re calling the main method and then… finishing. The string’s Future, provided by the global ExecutionContext, does the work of calling Console.println. This is great, because when we give up control over when someString is going to be there and when Console.println is going to be called, we let the system manage itself. In constrast, look what happens when we try to force the box open:

val stringFuture: Future[String] = Future.successful("hello world!")
val someString = Future.await(stringFuture)

In this case, we have to wait — keep a thread twiddling its thumbs — until we get someString back. We’ve opened the box, but we’ve had to commandeer the system’s resources to get at it.

Paresthesia answered 20/11, 2015 at 19:21 Comment(0)
B
2

It wasn't yet mentioned, so I want to emphasize the point of using Future with for-comprehension and the difference of sequential and parallel execution.

For example, for sequential execution:

object FuturesSequential extends App {

  def job(n: Int) = Future {
    Thread.sleep(1000)
    println(s"Job $n")
  }

  val f = for {
    f1 <- job(1)
    f2 <- job(2)
    f3 <- job(3)
    f4 <- job(4)
    f5 <- job(5)
  } yield List(f1, f2, f3, f4, f5)
  f.map(res => println(s"Done. ${res.size} jobs run"))
  Thread.sleep(6000) // We need to prevent main thread from quitting too early
}

And for parallel execution (note that the Future are before the for-comprehension):

object FuturesParallel extends App {

  def job(n: Int) = Future {
    Thread.sleep(1000)
    println(s"Job $n")
  }

  val j1 = job(1)
  val j2 = job(2)
  val j3 = job(3)
  val j4 = job(4)
  val j5 = job(5)

  val f = for {
    f1 <- j1
    f2 <- j2
    f3 <- j3
    f4 <- j4
    f5 <- j5
  } yield List(f1, f2, f3, f4, f5)
  f.map(res => println(s"Done. ${res.size} jobs run"))
  Thread.sleep(6000) // We need to prevent main thread from quitting too early
}
Biocatalyst answered 9/12, 2019 at 11:34 Comment(1)
Thanks for sharing this one !Sinking

© 2022 - 2024 — McMap. All rights reserved.