What is the pattern used in Scala to deal with the scenario:
You have a bunch of futures (they can be whatever, but for the sake of example...)
val aF = Future { true }
val bF = Future { Option(3) }
val cF = Future { myObject }
and you have some function that returns a future
def fooF: Future[SomeObject]
I want to do something like:
for {
a <- aF
b <- bF
c <- cF
} yield {
if (a) {
// do stuff with b & c
fooF
} else {
Future.successful(SomeObject)
}
}
I want to return a value of Future[SomeObject]
, but I call fooF inside of the yield statement, I will get a Future[Future[SomeObject]]
Here is another solution :
def doStuffWith(a: A, b: B, c: C): Future[SomeObject] = if (a) {
// do stuff with b & c
fooF
} else Future.successful(SomeObject)
for {
a <- aF
b <- bF
c <- cF
d <- doStuffWith(a, b, c)
} yield d
As discussed in @laughedelic answer, this is a subjective view, but I believe this way to be more readable, and maintainable (taking out the function always to unit test it, for instance).
In Scala 2.12 Future
has a flatten
method:
Creates a new future with one level of nesting flattened, this method is equivalent to flatMap(identity).
So you can write for { ... } yield { ... } flatten
.
In Scala <2.12 you can achieve the same with flatMap(identity)
(as mentioned above)
An alternative solution is to use flatMap
instead of for-comprehension:
aF.flatMap { a =>
if (a) {
(bF zip cF).flatMap { (b, c) =>
// do stuff with b & c
fooF
}
} else
Future.successful(SomeObject)
}
}