1

这是一个文体问题和我试图拓宽对 Scala 理解的尝试的结合。

我有一个包含 Future 的列表,我想计算期货的值,转换为 Option,并使用 for 理解来展平列表:

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)

我想要做的是让扁平化阶段进入理解,有人有什么建议吗?

4

3 回答 3

2

这样做是不正确的:

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

它似乎仅适用于您的情况,因为期货已经完成,这就是value定义其成员的原因。但是,在一般情况下,当您执行 for 理解时,它们可能尚未实现,因此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(最后一行),您将得到一个非空列表,因为 future 终于实现了。

要解决此问题,您将不得不使用 阻止(即等待所有期货都完成) ,或者使用类似orscala.concurrent.Await的东西留在异步世界中。例如,如果你想阻止,你可以这样做:Future.mapFuture.flatMap

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]]

于 2013-04-15T13:09:31.640 回答
1

这个想法是遍历Try对象,就好像它是Optionfor-comprehension 本身中的一个(即 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
于 2013-04-15T11:40:18.323 回答
0

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

执行此操作(并获得 a 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) => xs.flatMap(vxs => x.map(vx => vx :: vxs)) }


我们可以Future.sequence这样做:

Future.sequence(l)

这将返回 a Future[List[Int]],它仅在所有期货完成时才完成,并将包含成功完成的期货的所有值。

于 2013-04-16T01:14:26.197 回答