Scala Multiple Future 包裹在 Try

Posted

技术标签:

【中文标题】Scala Multiple Future 包裹在 Try【英文标题】:Scala Multiple Future wrapped in Try 【发布时间】:2017-08-06 05:46:35 【问题描述】:

假设我有许多 akka 服务,它们都返回一个 AbcDto 类型的案例类,包裹在 Try 中。

所以我使用map 调用所有这些服务并取回List[Future[Any]]

现在我使用Future.sequence 将其转换为Future[List[Any]]

如何解开我的最终结果列表?我只想在它们都是Success 时处理它们,即使一个失败我也想抛出一个错误。

我尝试将Future[List[Any]] 映射为:

val a: List[Future[Any]]
a.map 
    case r: List[Success[AbcDto]] => println("hello")

但这会产生错误: case r: List[Try[AbcDto]]。此时给出错误:非变量类型参数scala.util.Try[AbcDto] in type pattern List[scala.util.Try[AbcDto]]List[scala.util.Try[AbcDto]] 的底层)

【问题讨论】:

【参考方案1】:

因为所有的 akka 服务都返回 AbcDtowrapped 在一个尝试正确的 val a 类型应该是 List[Future[Try[AbcDto]]]。现在可以通过Future.sequence 和 flatMap 操作的组合来检查服务中的任何故障,如下所示,可以实现所需的结果。

val a: List[Future[Try[AbcDto]]] = ... 

val result: Future[List[AbcDto]]  = Future.sequence(a) flatMap 
    case r: List[Try[AbcDto]] @unchecked if r.find(!_.isSuccess).isDefined => Future.failed(new RuntimeException("not all results are successful"))
    case r => Future.successful(r.collect( case Success(x) => x))

【讨论】:

是的,但我使用未来序列将 List[Future[Try[AbcDto]]] 转换为 Future[List[Try[AbcDto]]] @Sidhant 我已经更新了答案。请看一下 案例 r:列表[Try[AbcDto]]。此时它给出错误:类型模式 List[scala.util.Try[AbcDto]] 中的非变量类型参数 scala.util.Try[AbcDto](List[scala.util.Try[AbcDto]] 的底层) 实际上在您的代码类型中 a 是 :List[Future[Any]] Future[Try[_]] 堆叠很少有意义,宁愿使用Future.fromTry 进行扁平化【参考方案2】:

Future[A]Try[A] 在演员上下文中是如此相似,以至于我认为没有必要从这些演员那里返回 Try[A]。如果成功,您只需返回A,这将是asking 一侧的Future[A],一个List,您可以在其中sequence 并获得Future[List[A]],如果发生单个故障,将包含第一个遇到的异常。这似乎正是您所要求的。

要将参与者的失败传达给asker,您应该发送akka.actor.Status.FailureThrowable 的相关实例。

附:关于使用 try-catch 是非惯用 Scala 的评论。它实际上是。以下是Try 创建的实现方式:

object Try 
  /** Constructs a `Try` using the by-name parameter.  This
   * method will ensure any non-fatal exception is caught and a
   * `Failure` object is returned.
   */
  def apply[T](r: => T): Try[T] =
    try Success(r) catch 
      case NonFatal(e) => Failure(e)
    

如您所见,它在内部使用try-catch。如果 Scala 标准库的作者对此感到满意,那么您也应该如此。 :)

【讨论】:

所以你说我应该把我的演员代码放在 try catch 块中。如果没有异常发件人!结果。但是如果发生异常我应该使用 akka.actor.Status.Failure 以便询问接收器不会超时? 是的,我就是这个意思。 但为此我需要将我的代码放在 try catch 块中。这有点反scala吧? 一点也不。如果需要处理异常,请使用 try-catch。看看Try 本身是如何实现的。或者您仍然可以通过Try 捕获异常,但立即通过Future.fromTry 将其转换为Future【参考方案3】:

如果我理解正确(你的问题类型有点混乱),

您从val responseFutures: List[Future[Any]] 开始,转换后您有一个val responsesFuture: Future[List[Any]]。流氓的答案是正确的,但它可以使用一些澄清:

您的编译器错误是由于Success 不是类,而是object 的提取器unapply 用于Try。因此,您不能以这种方式在类型提取中使用 in。

所以像case r: List[Try[AbcDto]] if r.forall(_.isSuccess) => println("hello") 这样的东西应该可以编译。但是,当AbcDto 被擦除时,您将收到有关擦除的编译器警告。因此@unchecked

更新

类型擦除意味着,编译器无法在编译时检查模式匹配中的类型参数类型。在您的情况下,所有编译都知道您的输入类型是Future[List[Try[Any]]]

所以

future.map 
  case _: List[Try[AbcDto]] => ???

将导致编译器警告,因为编译器只能看到。

future.map 
  case _: List[Try[_]] => ???

@unchecked 注释只是抑制了相应的编译器警告。

最后,在上面的模式匹配中,您只需将传入的任何内容转换为 Try[AbcDto],而没有任何编译时类型安全。

【讨论】:

AbcDto被擦除是什么意思?什么是未检查的? @Sidhant 用简短的解释更新了我的答案。有关更多信息,请参阅 scala 文档。

以上是关于Scala Multiple Future 包裹在 Try的主要内容,如果未能解决你的问题,请参考以下文章

在 Scala 中查找两个 Future[Int] 之间的区别

scala 报错 Multiple 'scala-library*.jar'

Scala之Future超时

scala中Future(Int)和FutureInt之间的区别

scala的Promise和Future的理解

发现 scala 类型不匹配 Future[A] 预期 Future[B]