Scala中使用for推导式过滤Future。

24

在下面的示例中,我遇到了异常java.util.NoSuchElementException: Future.filter predicate is not satisfied

当检查if( i == 2 )失败时,我希望得到结果Future( Test2 )。如何处理涉及组合 futures 的 for 推导中的 filter/if?

以下是在 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
4个回答

31

在我看来,这是一种更习惯用法的解决方案。该谓词函数创建一个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"

在你的 predicate 方法中,最好使用 Future.successful(()) 而不是 Future(()) - Ori Popowski

13
在你的`for-comprehension`中,你正在按`i == 2`进行过滤。因为`f1`的值不是两个,它将不会产生一个`Success`而是一个`Failure`。过滤器的谓词未满足,正如您的错误消息所告诉您的那样。然而,`f2.recover`返回一个新的`Future`。`f2`的值没有被操作。它仍然存储着`Failure`。这就是当你调用`f2.value`时得到错误消息的原因。

我所能想到的唯一替代方案就是在你的`for-comprehension`中使用一个`else`,就像这里所示的那样。
val f2 = for ( i <- f1) yield {
  if (i == 2) "Test1"
  else "Test2"
}
f2.value

您的f3.value返回Some(Success(Test2))


然而,如果f2因为“i <- f1”失败而失败,结果将不是“Test2”,而仍然是一个失败。 - Jürgen Strobel

10
当然,我自己想出了一个解决方案。也许还有更好、更符合习惯用语的解决方案吗?
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

1
在文档中也提到了:http://docs.scala-lang.org/overviews/core/futures.html#functional_composition_and_forcomprehensions 当我再看一遍时。 - Magnus
1
我认为在这里使用recover是可以的。如果你想让你的恢复仅适用于基于过滤器谓词的异常,那么你应该将case _更改为case ex:NoSuchElementException,其中NoSuchElementException来自于java.util - cmbaxter
Jürgen:这是一个六个月前的问题,上面的代码是从REPL会话中摘取的示例,因此它旨在提高清晰度。您评论说您可以在Scala中将语句括在括号中?我没有看到您在这里做出的贡献。 - Magnus

0

我喜欢 @pkinsky 的想法,并进行了一些改进。我放弃了创建 Exception 对象的代码,像这样:

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

shouldTrue 函数是使用 lihaoyi 的 sourcecode 库实现的:

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}"))

然后它会自动生成更有意义的异常信息:

java.lang.Exception: i == 2 returns false
    at \path\to\example.scala:17

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接