Подтвердить что ты не робот

Scala Будущее с фильтром для понимания

В приведенном ниже примере я получаю исключение java.util.NoSuchElementException: Future.filter predicate is not satisfied

Я хочу получить результат Future( Test2 ), когда проверка if( i == 2 ) завершается с ошибкой. Как обрабатывать фильтр/если он используется для понимания, связанного с составлением фьючерсов?

Ниже приведен упрощенный пример, который работает в Scala REPL.

Код:

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
4b9b3361

Ответ 1

В for-comprehension вы фильтруете i == 2. Поскольку значение f1 не равно двум, оно не даст значение Success, а вместо этого Failure. Предикат фильтра не выполняется, как сообщает ваше сообщение об ошибке. Однако f2.recover возвращает новый Future. Значение f2 не обрабатывается. Он по-прежнему сохраняет Failure. Именно по этой причине вы получаете сообщение об ошибке при вызове f2.value.

Единственная альтернатива, о которой я могу думать, будет использовать else в вашем for-comprehension, как показано здесь.

val f2 = for ( i <- f1) yield {
  if (i == 2) "Test1"
  else "Test2"
}
f2.value

Это вернет Some(Success(Test2)), как делает ваш f3.value.

Ответ 2

Это, по-моему, более идиоматическое решение. Эта предикатная функция создает либо Future[Unit], либо неудавшееся будущее, содержащее ваше исключение. Для вашего примера это приведет либо к Success("Test1"), либо к Failure(Exception("Test2")). Это немного отличается от "Test1" и "Test2", но я считаю этот синтаксис более полезным.

def predicate(condition: Boolean)(fail: Exception): Future[Unit] = 
    if (condition) Future( () ) else Future.failed(fail)

Вы используете его следующим образом:

val f2 = for {
  i <- f1
  _ <- predicate( i == 2 )(new Exception("Test2"))
  j <- f3  // f3 will only run if the predicate is true
} yield "Test1"

Ответ 3

Конечно, я сам разобрался в одном решении. Возможно, есть лучшие, более идиоматические решения?

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