Dans l'exemple ci-dessous, je reçois l'exception Java.util.NoSuchElementException: Future.filter predicate is not satisfied
Je veux avoir le résultat Future( Test2 )
lorsque la vérification if( i == 2 )
échoue. Comment est-ce que je manipule le filtre/si dans une compréhension qui traite de la composition de futurs?
Vous trouverez ci-dessous un exemple simplifié qui fonctionne dans le 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
Dans votre for-comprehension
, vous filtrez par i == 2
. Étant donné que la valeur de f1
n’est pas deux, elle ne générera pas une Success
mais plutôt une Failure
. Le prédicat du filtre n'est pas satisfait, comme l'indique votre message d'erreur. Cependant, f2.recover
renvoie une nouvelle Future
. La valeur de f2
n'est pas manipulée. Il stocke toujours la Failure
. C'est la raison pour laquelle vous recevez le message d'erreur lorsque vous appelez f2.value
.
La seule alternative à laquelle je puisse penser serait d'utiliser une else
dans votre for-comprehension
, comme indiqué ici .
val f2 = for ( i <- f1) yield {
if (i == 2) "Test1"
else "Test2"
}
f2.value
Cela retournera Some(Success(Test2))
comme votre f3.value
le fait.
C'est une solution plus idiomatique, à mon avis. Cette fonction de prédicat crée soit un Future[Unit]
, soit un futur échoué contenant votre exception. Pour votre exemple, cela entraînerait soit une Success("Test1")
, soit une Failure(Exception("Test2"))
. Ceci est légèrement différent de "Test1" et "Test2", mais je trouve cette syntaxe plus utile.
def predicate(condition: Boolean)(fail: Exception): Future[Unit] =
if (condition) Future( () ) else Future.failed(fail)
Vous l'utilisez comme ceci:
val f2 = for {
i <- f1
_ <- predicate( i == 2 )(new Exception("Test2"))
j <- f3 // f3 will only run if the predicate is true
} yield "Test1"
Bien sûr, j'ai trouvé une solution moi-même. Peut-être y at-il de meilleures solutions, plus idiomatiques?
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
J'ai bien aimé l'idée de @pkinsky et fait quelques améliorations. J'ai abandonné le code pour créer un objet Exception
comme ceci:
val f2 = for {
i <- f1
_ <- shouldTrue( i == 2 )
j <- f3 // f3 will only run if the predicate is true
} yield "Test1"
La fonction shouldTrue
est implémentée à l’aide de code source bibliothèque de lihaoyi:
def shouldTrue(condition: sourcecode.Text[Boolean])(implicit enclosing: sourcecode.Enclosing, file: sourcecode.File, line: sourcecode.Line): Future[Unit] =
if (condition.value) Future.successful( () ) else Future.failed(new Exception(s"${condition.source} returns false\n\tat ${file.value}:${line.value}"))
Ensuite, il génère automatiquement un message d'exception plus significatif:
Java.lang.Exception: i == 2 returns false
at \path\to\example.scala:17