Question

I need to get back a variable that was changed inside a Future's onSuccess block. Here is my sample code.

def futureFunction(arg:Any):Any = { 
  implicit val timeout = Timeout(10 seconds) 
  var ret:Any = None
  val f: Future[Any] = someActor ? arg
    f.onSuccess {
      case a:String => {
        ret = 1 
        println(ret) //prints "1" 
      }
      case a:Int => { 
        ret = 2 
        println(ret) //prints "2"
      }
    }
  ret //returns None
  }

ret returns as None and does not change to 1 or 2. So does this mean that value of variable ret inside the onSuccess block cannot be returned? Yet, this seems to work:

var a : Any = None

{
  a = 1
  println("inside "+ a ) //prints "inside 1"
}

println(a)   // prints "1"

What can I do in the future onSuccess block so that value of ret is returned as 1or 2 depending on whether the future's result is an integer or a string? What are the scoping rules in this case for scala. (I am using akka). Any help is appreciated. Thanks.

Was it helpful?

Solution

The problem here is not of scoping but of timing.

  1. First you're initializing ret to None.
  2. Then you're sending the message arg to someActor, which returns a future.
  3. After that, you set up a function to be called when the future completes, which, if all goes well, will assign a value of 1 or 2 to ret at some point in the future.
  4. Last of all, you immediately return the value of ret, which is still None.
  5. And at some point in the future, the future completes, and your onSuccess function runs changing the value of ret and then printing it. But of course that can't go back in time and change the value that was already returned earlier.

So, to answer your question, there is nothing you can do in the onSuccess that will go back in time and change the already returned value. There are two alternatives though. One alternative would be to block until the future completes, then determine whether to return 1 or 2. The other is to return a Future[Int] so the caller can decide whether or not they want to block on the result. The code for that is actually simpler than what you have:

def futureFunction(arg:Any):Future[Int] = { implicit val timeout = Timeout(10 seconds) val f: Future[Any] = someActor ? arg f.map { case a:String => 1 case a:Int => 2 } }

If you do need to block, you can use Await.result, either inside or outside futureFunction depending on where you want to block.

OTHER TIPS

In futureFunction, ret is returned before the onSuccess handler runs. ret will become 1 or 2 when that happens, but it is too late at that point.

What you want to use is Future.map

someActor ? arg map {
  case a:String => 1
  case a:Int => 2 
}

Note that futureFunction will either have to return a Future, or you will have to make a call to Await.result inside the function to block until the result is available.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top