无法理解尝试理解

时间:2013-04-15 11:23:11

标签: list scala try-catch future for-comprehension

这是一个风格问题和我试图扩大Scala理解的尝试的组合。

我有一个包含Future的列表,我想计算期货的价值,转换成Option,并使用for comprehension展平列表:

import scala.util.Try
import scala.concurrent._
import ExecutionContext.Implicits.global

val l= List(Future.successful(1),Future.failed(new IllegalArgumentException))

implicit def try2Traversible[A](xo: Try[A]): Iterable[A] = xo.toOption.toList

val res = for{f <- l; v <- f.value} yield v

scala> res: List[scala.util.Try[Int]] = List(Success(1), Failure(java.lang.IllegalArgumentException))

res.flatten
res16: List[Int] = List(1)

我想做的是让平坦的舞台进入理解状态,任何人都有任何建议吗?

3 个答案:

答案 0 :(得分:2)

这样做不正确:

for{f <- l; v <- f.value} yield v

它似乎只适用于您的情况,因为期货已经完成,这就是定义value成员的原因。 但是在一般情况下,当您执行for comprehension时,它们可能尚未完成,因此value将返回None (尽管在某些时候他们最终会实现)。 例如,在REPL中尝试这个:

val f1 = Future{ 
  Thread.sleep(3000) // Just a test to illustrate, never do this!
  1
}
val f2 = Future{ 
  Thread.sleep(3000) // Just a test to illustrate, never do this!
  throw new IllegalArgumentException
}

val l = List( f1, f2 )
for{f <- l; v <- f.value} yield v

结果是一个空列表,因为l中的所有未来都没有完成。然后等一下(最多3秒)并重新执行for comprehension(最后一行),你会得到一个非空列表,因为期货终于已经完成了。

要解决此问题,您必须使用scala.concurrent.Await阻止(即等待所有未来的实现),或者使用Future.map或{Future.flatMap之类的内容保持异步世界{1}}。 例如,如果你想阻止,你可以这样做:

Await.result( Future.sequence( l ), duration.Duration.Inf )

Await.result等待未来的结果,允许从异步世界进入同步世界。以上结果是List[Int] 现在的问题是你丢失了失败的情况(结果不是你想要的List[Try[Int]]),并且实际上会重新抛出第一个异常。 要解决此问题,您可以使用我在另一个答案中发布的辅助方法:https://stackoverflow.com/a/15776974/1632462 使用它,您可以:

Await.result( Future.sequence( l map mapValue ), duration.Duration.Inf )

这将等到所有期货都完成(使用正确的值或出错)并返回预期的List[Try[Int]]

答案 1 :(得分:1)

这个想法是遍历Try对象,好像它是for-comprehension本身中的Option(即0或1个元素集合)。 要进行此遍历,必须进行从Try类型到Option类型的转换。

这应该有效:

implicit def try2option[A](xo: Try[A]) = xo.toOption

val res = for (f <- l; t <- f.value; x <- t) yield x

答案 2 :(得分:0)

您应该在最终结果周围保留Future以保留计算的异步性。

执行此操作的好方法(并获得Future[List[Int]])将是(可能是您尝试过的):

for {
  f <- l    // Extract individual future
  v <- f    // Extract value from future
} yield v

不幸的是,这转化为:

l.flatMap(f => f.map(v => v))

哪个不起作用,因为Future不会继承GenTraversableOnce(可能不应该),但List需要此flatMap的特征。

<德尔> 但是,我们可以手动执行此操作:     val res = l.foldRight(Future.successful(List.empty [Int])){       case(x,xs)=&gt; xs.flatMap(vxs =&gt; x.map(vx =&gt; vx :: vxs))     }


我们可以使用Future.sequence来执行此操作:

Future.sequence(l)

这将返回Future[List[Int]],仅在所有期货完成后才会完成,并且将包含成功完成的所有期货价值。