Reputation: 937
I have 2 methods that return Future[Option[String]]
and need to invoke the second method if the first method returns a Future[None]
. Following is the code I have so far:
def method1(): Future[Option[String]] = Future(None)
def method2(): Future[Option[String]] = Future(Some("test"))
val resultFuture = method1().flatMap(option =>
if (option.isDefined) { // Alternatives?
Future(option)
} else {
method2()
})
val durationSeconds = {
import scala.concurrent.duration._
import scala.language.postfixOps
20 seconds
}
val result = Await.result(resultFuture, durationSeconds)
println(result) // Prints Some(test) as expected
I think this solution is deficient because
I have tried
method1().flatMap(option => option.fold(method2)(value => Future(Option(value))))
. But this has the same issue.option.orElse(...)
, but it needs to return an Option[B]
not Future[Option[B]]
.Appreciate any pointers on mapping the Future[None]
without explicitly mapping/passing-through the Future[Some[String]]
.
Upvotes: 4
Views: 426
Reputation: 22605
Future.apply
creates a new future and schedules it to run on the execution context, which is indeed costly, so you're right it's not the best solution.
Instead of Future.apply
you could just use Future.sucessful
, which creates already resolved Future
and is a very cheap operation. You could also make your code (arguably) more readable with pattern matching and for-comprehension:
for{
m1 <- method1()
result <- m1 match {
case x @ Some(_) => Future.successful(x) //just lifts value into Future context
case None => method2()
}
} yield result
You could also use a flat map with pattern matching:
method1().flatMap{
case x @ Some(_) => Future.successful(x)
case None => method2()
}
Another solution you might consider is using monad transformer OptionT
which simplifies operations on nested monad stack like Future[Option[T]]
. Both scalaz and cats provide its own implementations of OptionT
. Below an example with cats
:
import cats.data.OptionT
import cats.implicits._
OptionT(method1()).orElseF(method2()).value
Upvotes: 4
Reputation: 51271
Here's one alternative.
val resultFuture :Future[Option[String]] = method1().flatMap(
_.fold(method2())(str => Future.successful(Some(str))))
It still re-wraps the populated Option
but there's less overhead as a Future.successful()
.
Upvotes: 2