3
votes

Given that we must avoid...

1) Modifying state 2) Blocking

...what is a correct end-to-end usage for a Future?

The general practice in using Futures seems to be transforming them into other Futures by using map, flatMap etc. but it's no good creating Futures forever.

Will there always be a call to onComplete somewhere, with methods writing the result of the Future to somewhere external to the application (e.g. web socket; the console; a message broker) or is there a non-blocking way of accessing the result?

All of the information on Futures in the Scaladocs - http://docs.scala-lang.org/overviews/core/futures.html seem to end up writing to the console. onComplete doesn't return anything, so presumably we have to end up doing some "fire-and-forget" IO.

e.g. a call to println

f onComplete {
  case Success(number) => println(number)
  case Failure(err) => println("An error has occured: " + err.getMessage)
}

But what about in more complex cases where we want to do more with the result of the Future?

As an example, in the Play framework Action.async can return a Future[Result] and the framework handles the rest. Will it eventually have to expect never to get a result from the Future?

We know the user needs to be returned a Result, so how can a framework do this using only a Unit method?

Is there a non-blocking way to retrieve the value of a future and use it elsewhere within the application, or is a call to Await inevitable?

4
result (goo.gl/GeW9Pl) does not return Unit. You have to see a Future as just a way to spawn computation on a different thread (I know I'm oversimplifying). - mfirry
Isn't using Await blocking? Or is the idea that we will always have to block a thread somewhere. - user2232887
Await is blocking. I'm just saying one way is to just wait for the computation to finish but meanwhile you can do other things or define computation on a un-finished Future. - mfirry

4 Answers

2
votes

Best practice is to use callbacks such as onComplete, onSuccess, onFailure for side effecting operations, e.g. logging, monitoring, I/O.

If you need the continue with the result of of your Future computation as opposed to do a side-effecting operation, you should use map to get access to the result of your computation and compose over it.

1
votes

Future returns a unit, yes. That's because it's an asynchronous trigger. You need to register a callback in order to gather the result.

From your referenced scaladoc (with my comments):

// first assign the future with expected return type to a variable.
val f: Future[List[String]] = Future {
  session.getRecentPosts
}
// immediately register the callbacks
f onFailure {
  case t => println("An error has occurred: " + t.getMessage)
}
f onSuccess {
  case posts => for (post <- posts) println(post) 
}

Or instead of println-ing you could do something with the result:

f onSuccess {
  case posts: List[String] => someFunction(posts)
}
0
votes

Try this out:

import scala.concurrent.duration._
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global

val f: Future[Int] = Future { 43 }

val result: Int = Await.result(f, 0 nanos)

So what is going on here?

You're defining a computation to be executed on a different thread.

So you Future { 43 } returns immediately.

Then you can wait for it and gather the result (via Await.result) or define computation on it without waiting for it to be completed (via map etc...)

0
votes

Actually, the kind of Future you are talking about are used for side-effects. The result returned by a Future depends its type :

val f = Future[Int] { 42 }

For example, I could send the result of Future[Int] to another Future :

val f2 = f.flatMap(integer => Future{ println(integer) }) // may print 42

As you know, a future is a process that happens concurrently. So you can get its result in the future (that is, using methods such as onComplete) OR by explicitly blocking the current thread until it gets a value :

import scala.concurrent.Await
import akka.util.Timeout
import scala.concurrent.duration._

implicit val timeout = Timeout(5 seconds)
val integer = Await.result(Future { 42 }, timeout.duration)

Usually when you start dealing with asynchronous processes, you have to think in terms of reactions which may never occur. Using chained Futures is like declaring a possible chain of events which could be broken at any moment. Therefore, waiting for a Future's value is definitely not a good practice as you may never get it :

val integer = Await.result(Future { throw new RuntimeException() }, timeout.duration) // will throw an uncaught exception

Try to think more in terms of events, than in procedures.