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
21
demandé sur Jeffrey Chung 2013-07-26 02:07:19

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 .

11
répondu tgr 2017-05-23 12:34:19

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"
28
répondu pkinsky 2015-03-17 22:50:14

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
8
répondu Magnus 2013-07-25 22:15:17

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
0
répondu pocorall 2018-08-20 04:47:26