【问题标题】:Scala recover or recoverWithScala恢复或recoverWith
【发布时间】:2016-08-03 17:58:37
【问题描述】:

我们正在公司中使用 Scala 开发一些系统,但我们有一些疑问。我们正在讨论如何映射未来的异常,我们不知道何时应该使用选项 1 或选项 2。

val created: Future[...] = ???

选项 1:

val a = created recover {   
  case e: database.ADBException =>
    logger.error("Failed ...", e)
    throw new business.ABusinessException("Failed ...", e) 
}

选项 2:

val a = created recoverWith {   
  case e: database.ADBException =>
    logger.error("Failed ...", e)
    Future.failed(new business.ABusinessException("Failed ...", e))
}

有人能解释一下我应该什么时候做选项 1 或选项 2 吗?有什么区别?

【问题讨论】:

  • 对于发现这个问题的其他人来说,3年后有点迂腐,一般来说“recover”和“recoverWith”的重点不是简单地将你的异常从一种类型转换为另一种类型,而是通过以不同的方式执行任务来从失败中恢复,这样你就不会再遇到失败了。在这种情况下,您对“recover”与“recoverWith”的使用取决于 new 恢复任务是发生在 Future 内部还是没有 Future。
  • @Azuaron 所以如果你只是需要转换异常,你推荐使用什么来代替recoverstackoverflow.com/q/59099000/14955
  • 不幸的是,recover 仍然是几乎唯一的选择,但重要的是要了解您正在利用什么。
  • @Thilo 有transform 来转换异常。

标签: scala error-handling future


【解决方案1】:

嗯,答案在 scaladocs 中有明确描述:

  /** Creates a new future that will handle any matching throwable that this
   *  future might contain. If there is no match, or if this future contains
   *  a valid result then the new future will contain the same.
   *
   *  Example:
   *
   *  {{{
   *  Future (6 / 0) recover { case e: ArithmeticException => 0 } // result: 0
   *  Future (6 / 0) recover { case e: NotFoundException   => 0 } // result: exception
   *  Future (6 / 2) recover { case e: ArithmeticException => 0 } // result: 3
   *  }}}
   */
  def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = {

  /** Creates a new future that will handle any matching throwable that this
   *  future might contain by assigning it a value of another future.
   *
   *  If there is no match, or if this future contains
   *  a valid result then the new future will contain the same result.
   *
   *  Example:
   *
   *  {{{
   *  val f = Future { Int.MaxValue }
   *  Future (6 / 0) recoverWith { case e: ArithmeticException => f } // result: Int.MaxValue
   *  }}}
   */
  def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = {

recover 为您将纯结果包装在 Future 中(map 的类似物),而recoverWith 期望 Future 作为结果(flatMap 的类似物)。

所以,这里是经验法则:

如果您使用已经返回 Future 的内容进行恢复,请使用 recoverWith,否则使用 recover

更新 在您的情况下,首选使用 recover,因为它为您将异常包装在 Future 中。否则没有性能提升或任何东西,所以你只是避免一些样板。

【讨论】:

    【解决方案2】:

    使用recoverWith 要求您返回一个包装好的未来,使用recover 要求您抛出一个异常。

    .recoverWith # => Future.failed(t)
    .recover # => throw t
    

    我更喜欢使用recoverWith,因为我认为函数式编程更喜欢返回对象而不是抛出异常,这不是一种函数式风格,即使它是内部代码块,我认为它仍然成立..

    但是,如果我在恢复块中有一段可能引发异常的内部代码,那么在这种情况下,与其捕获它并用Future 包装它,或者尝试它,我还不如运行它一段代码与recover 结合使用,它将为您处理异常包装,这将使代码更具可读性和紧凑性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-21
      • 2017-12-01
      • 2017-10-24
      • 1970-01-01
      • 1970-01-01
      • 2013-08-05
      相关资源
      最近更新 更多