Scala Future avec filtre pour la compréhension
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 )
quand le contrôle if( i == 2 )
échoue. Que dois-je faire avec filter/if À l'intérieur d'un pour la compréhension qui traite de la composition des futurs?
ci-dessous est un exemple simplifié qui fonctionne dans la réponse Scala.
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
4 réponses
dans votre for-comprehension
, vous filtrez par i == 2
. Parce que la valeur de f1
n'est pas deux, il ne donnera pas un Success
mais plutôt un Failure
. Le prédicat du filtre n'est pas satisfait, comme votre message Error vous le dit. Cependant, f2.recover
renvoie un nouveau Future
. La valeur de f2
n'est pas manipulé. Il conserve encore le Failure
. C'est la raison pour laquelle vous obtenez le message d'erreur lorsque vous appelez f2.value
.
la seule alternative que je puisse imaginer serait d'utiliser un else
dans votre for-comprehension
comme indiqué ici .
val f2 = for ( i <- f1) yield {
if (i == 2) "Test1"
else "Test2"
}
f2.value
ça va retourner Some(Success(Test2))
comme votre f3.value
.
C'est une solution plus idiomatique, à mon avis. Cette fonction de prédicat crée soit un Future[Unit]
ou un futur raté contenant votre exception. Dans votre exemple, cela donnerait un Success("Test1")
ou un Failure(Exception("Test2"))
. C'est légèrement différent de "Test1" et "Test2", mais je trouve que cette syntaxe est 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 moi-même trouvé une solution. Peut-être y a-t-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 aimé l'idée de @pkinsky, et fait un peu d'amélioration. J'ai laissé tomber le code pour créer Exception
objet comme ceci:
val f2 = for {
i <- f1
_ <- shouldTrue( i == 2 )
j <- f3 // f3 will only run if the predicate is true
} yield "Test1"
shouldTrue
la fonction est implémentée en utilisant le sourcecode bibliothèque:
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}"))
alors il génère automatiquement un message d'exception plus significatif:
java.lang.Exception: i == 2 returns false
at \path\to\example.scala:17