这是一个风格问题和我试图扩大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)
我想做的是让平坦的舞台进入理解状态,任何人都有任何建议吗?
答案 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
的特征。
我们可以使用Future.sequence
来执行此操作:
Future.sequence(l)
这将返回Future[List[Int]]
,仅在所有期货完成后才会完成,并且将包含成功完成的所有期货价值。