Scala Future with filter in for comprehension
Solution 1
In your for-comprehension
, you are filtering by i == 2
. Because the value of f1
is not two, it will not yield a Success
but instead a Failure
. The predicate of the filter is not satisfied, as your errror message tells you. However, f2.recover
returns a new Future
. The value of f2
is not manipulated. It still stores the Failure
. That is the reason you get the error message when you call f2.value
.
The only alternative I can think of would be using an else
in your for-comprehension
as shown here.
val f2 = for ( i <- f1) yield {
if (i == 2) "Test1"
else "Test2"
}
f2.value
This will return Some(Success(Test2))
as your f3.value
does.
Solution 2
This is a more idiomatic solution, in my opinion. This predicate function creates either a Future[Unit]
or a failed future containing your exception. For your example, this would result in either a Success("Test1")
or a Failure(Exception("Test2"))
. This is slightly different from "Test1" and "Test2", but I find this syntax to be more useful.
def predicate(condition: Boolean)(fail: Exception): Future[Unit] =
if (condition) Future( () ) else Future.failed(fail)
You use it like this:
val f2 = for {
i <- f1
_ <- predicate( i == 2 )(new Exception("Test2"))
j <- f3 // f3 will only run if the predicate is true
} yield "Test1"
Solution 3
Of course I figured out one solution myself. Perhaps there are better, more idiomatic, solutions?
import scala.concurrent.Future
import scala.util.{ Try, Success, Failure }
import scala.concurrent.ExecutionContext.Implicits.global
val f1 = Future( 1 )
val f2 = for {
i <- f1
if( i == 2 )
} yield "Test1"
val f3 = f2.recover{ case _ => "Test2" }
// OR val f3 = f2.fallbackTo( Future( "Test2" ) )
f3.value
Related videos on Youtube
Magnus
Updated on August 21, 2020Comments
-
Magnus over 3 years
In the example below I get the exception
java.util.NoSuchElementException: Future.filter predicate is not satisfied
I want to have the result
Future( Test2 )
when the checkif( i == 2 )
fails. How do I handle filter/if within a for comprehension that deals with composing futures?Below is a simplified example that works in the Scala REPL.
Code:
import scala.concurrent.Future import scala.util.{ Try, Success, Failure } import scala.concurrent.ExecutionContext.Implicits.global val f1 = Future( 1 ) val f2 = for { i <- f1 if( i == 2 ) } yield "Test1" f2.recover{ case _ => "Test2" } f2.value
-
Magnus almost 11 yearsIt is mentioned in the docs as well: docs.scala-lang.org/overviews/core/… no when I take a second look.
-
cmbaxter almost 11 yearsI think using
recover
here is okay. If you want your recover to only apply to the filter predicate based exception, then you should change thecase _
tocase ex:NoSuchElementException
whereNoSuchElementException
comes fromjava.util
. -
Jürgen Strobel over 10 yearsHowever if f2 fails because of "i <- f1" failing, the result will not be "Test2" but still a Failure.
-
Magnus over 10 yearsJürgen: This is a six months old question, the code above was an example taken out of a REPL session, so it is geared towards clarity. You're commenting on that you can put parenthesis around statements in Scala? Not seeing what your contribution is here.
-
Ori Popowski almost 4 yearsIn your
predicate
method, you're better off usingFuture.successful(())
thanFuture(())