I have a scenario in my code where I need to make a Future
fail based on a successful result that contains a certain value. I can make this work just fine via flatMap
, but I want to know if there is a cleaner way to make this work. First, a very simplified example:
import concurrent._
case class Result(successful:Boolean)
object FutureTest {
def main(args: Array[String]) {
import ExecutionContext.Implicits._
val f = Future{Result(false)}.flatMap{ result =>
result match{
case Result(false) => Promise.failed(new Exception("The call failed!!")).future
case _ => Promise.successful(result).future
}
}
f onFailure{
case x => println(x.getMessage())
}
}
}
So in my example here, I want the Future
to be failed if the Result
returned has a value of false
for its success indicator. As I mentioned, I can make this work okay with flatMap
, but the line of code I would like to eliminate is:
case _ => Promise.successful(result).future
This statement seems unnecessary. The behavior that I would like is to be able to define the condition and if it evaluates to true, allow me to return a different Future
as I am doing, but if it's not true, just leave things as they are (sort of like PartialFunction
semantics. Is there a way to do this that I'm just not seeing? I've looked at collect
and transform
and those don't seem to be the right fit either.
Edit
After getting the map
suggestion from @Rex Kerr and @senia, I created a PimpedFuture
and an implicit conversion to pretty up the code a bit like so:
class PimpedFuture[T](f:Future[T])(implicit ex:ExecutionContext){
def failWhen(pf:PartialFunction[T,Throwable]):Future[T] = {
f map{
case x if (pf.isDefinedAt(x)) => throw pf(x)
case x => x
}
}
}
The implicit
implicit def futToPimpedFut[T](fut:Future[T])(implicit ec:ExecutionContext):PimpedFuture[T] = new PimpedFuture(fut)
And the new handling code:
val f = Future{ Result(false) } failWhen {
case Result(false) => new Exception("The call failed!!")
}
I think this is a little cleaner while still utilizing the suggestion to use map
.
This Future. sequence() function converts a list of Futures into a single Future that means collections of Futures into a single Future. In simple words, List[Future[T]] ======> Future[List[T]] . It is also known as composing Futures.
Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.
recover allows you to emit a final element and then complete the stream on an upstream failure. Deciding which exceptions should be recovered is done through a PartialFunction . If an exception does not have a matching case the stream is failed.
You can do this with less effort with map
:
val f = Future{ Result(false) } map {
case Result(false) => throw new Exception("The call failed!!")
case x => x
}
but you still need to explicitly mention that you're passing the identity through.
Note that that you should not use andThen
in such cases. andThen
is for side effects, not for changing the result (unlike the Function1
method of the same name).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With