This is the third of several posts describing protips for scala.concurrent.Future
.
For the previous post, click here.
Unapplying Future.apply (sometimes)
I quite frequently encounter code which looks a bit like this:
import scala.concurrent._
def doSomething(someParam: SomeType)(implicit ec: ExecutionContext): Future[ResultType] =
if (someParam == SomeValue) Future(SomeConstant)
else Future(performSomeCalculation())
So what’s wrong with that? From a correctness point-of-view: nothing. However, if performance is a part of correctness—or at the very least part of availability—then understanding the implications, and alternatives, might be interesting!
Let’s start with: what does Future.apply
do?
According to its ScalaDoc, it does the following:
Starts an asynchronous computation and returns a Future instance with the result of that computation.
Alright, so it “starts a computation”. Furthermore the scaladoc says:
The following expressions are equivalent:
val f1 = Future(expr)
val f2 = Future.unit.map(_ => expr)
` The result becomes available once the asynchronous computation is completed.
Interesting! So whenever you write Future(something)
it is actually equivalent to having written Future.unit.map(_ => something)
.
So the problem with the code is that we’re—needlessly—starting an asynchronous computation in order to create a Future
with an already known value.
What can we do instead of Future.apply
?
The Future
companion object sports 3 additional types of “constructors” for values of Future
type:
- successful
Creates an already completed Future with the specified result.
- failed
Creates an already completed Future with the specified exception.
- fromTry
Creates an already completed Future with the specified result or exception.
Armed with this knowledge, we can rewrite the example as follows:
import scala.concurrent._
def doSomething(someParam: SomeType)(implicit ec: ExecutionContext): Future[ResultType] =
if (someParam == SomeValue) Future.successful(SomeConstant)
else Future(performSomeCalculation())
This avoids starting an asynchronous computation in the case where someParam == SomeValue
.
TL;DR:
Prefer Future.successful
, Future.failed
, and Future.fromTry
when you need to create an instance of Future
and you already have the value.
Click here for the next part in this blog series.
Cheers,
√