当前位置: 首页 > 知识库问答 >
问题:

Scala 异常处理,具有一系列依赖的未来和非未来方法调用

岑明辉
2023-03-14

我有一个方法可以返回< code>Future -成功或失败,甚至可以抛出一个异常。我可以通过在整个方法上放置try catch块并始终返回Future来避免这种情况,但我现在想避免这种情况。我对调用这样的方法没有什么问题:

1)在调用方代码中,如果我使用map,我希望执行一个方法,并期望一个未来或异常,我试图通过以下方式处理:

object ETLCoordinator {      

  private def getBusinessListFromModules(modulePaths: Iterable[File]) : Future[String] = {
    implicit val ec = ExecutionContext.global
    println("Inside getBusinessListFromModules..")
    throw new java.lang.RuntimeException("failed to get businesses") //Exception is thrown before future was constructed
    Future("ok")
  }

  def main(args: Array[String]) {

    println("Inside Future Test..")
    implicit val ec = ExecutionContext.global
    val modulePaths = Iterable(new File("mdrqaint/MDR/QAINT/QAINTX"))

    val fut1 = getBusinessListFromModules(modulePaths) //This is outside of try and which should be okay
    try { 
      fut1.map { res =>
        println("things after Successful fut1")
      }.recover{
        case t: Throwable => println("Failed future in fut1: "+ t.getMessage)
      }  
    } catch {
      case t: Throwable => println("Exception in fut1: "+ t.getMessage)
    }
  }    
}

输出:(上面没有执行恢复或捕获块)

Inside Future Test..
Inside getBusinessListFromModules..
Exception in thread "main" java.lang.RuntimeException: failed to get businesses

但如果我将<code>val fut1=getBusinessListFromModules(modulePath)</code>放在Try块中,则异常在Catch块中被捕获,我得到输出:

Inside Future Test..
Inside getBusinessListFromModules..
Exception in fut1: failed to get businesses

这是为什么?我认为未来的执行会在调用它的一些方法时发生,如map、flatmap、onSuccess、onComplete等。在这种情况下,对<code>map</code>的调用已经在Try块中。

2)定义和调用此类方法的更好方法是什么?在调用者中尝试/捕获块还是在方法本身中尝试/捕获?或任何其他方式。我尝试在Future中包装调用方法,因此我在调用者中获得Future[Future[String]]。我能够避免所有的try-catch。

val fut1 = Future(getBusinessListFromModules(modulePaths))
//try {
  fut1.map { res =>
    res.map{ str =>
      println("things after Successful fut1")  
    }.recover{
      case t: Throwable => println("Failed in future of fut1: "+ t.getMessage)
    } 
    println("things after Successful fut1 wrapper")
  }.recover{
    case t: Throwable => println("Failed to create future in fut1: "+ t.getMessage)
  } 

3) 如果中间有另一个方法委托给getBusinessListFromModules,但它本身不是未来的方法。

object ETLController {

  private def getBusinessListFromModules(modulePaths: Iterable[File]) : Future[String] = {
    implicit val ec = ExecutionContext.global
    println("Inside getBusinessListFromModules..")
    //throw new java.lang.RuntimeException("failed to get businesses")
    Future("ok")
  }

  private def callGetBusList(modulePaths: Iterable[File]) : String = {

    implicit val ec = ExecutionContext.global
    val etlF = getBusinessListFromModules(modulePaths)    

    etlF onComplete { 
      case Success(itr) => {
        println("Future getBusinessListFromModules success: "+ itr)
        throw new java.lang.RuntimeException("RTE from callGetBusList")
      }
      case Failure(t) => {
        println("Future getBusinessListFromModules throws an error")
      }
    }

    "callGetBusList was a success"
  }

  def main(args: Array[String]) {

    println("Inside Future Test..")
    implicit val ec = ExecutionContext.global
    val modulePaths = Iterable(new File("mdrqaint/MDR/QAINT/QAINTX"))
    try {
      val fut = Future(callGetBusList(modulePaths))
      fut.map { res =>
        println("successful future!")
      }.recover{
        case t: Throwable => println("Failed future: "+ t.getMessage)
      }
    } catch {
      case t: Throwable =>   println("callGetBusList failed:" + t.getMessage)
    }

  }    
}

输出:(无恢复或捕获块执行!

Inside Future Test..
Inside getBusinessListFromModules..
Future getBusinessListFromModules success: ok
java.lang.RuntimeException: RTE from callGetBusList
    at ..
successful future!

我甚至尝试双重包装未来调用:

val fut = Future(Future(callGetBusList(modulePaths)))
fut.map { res =>
  res.map { str =>
    println("successful inner future! "+ str)
  }.recover{
    case t: Throwable => println("Failed inner future: "+ t.getMessage)
  }
  println("successful outer future!")
}.recover{
  case t: Throwable => println("Failed outer future: "+ t.getMessage)
}

输出:

Future getBusinessListFromModules success: ok
java.lang.RuntimeException: RTE from callGetBusList
    at 
successful inner future! callGetBusList was a success
successful outer future!

我得到了“callGetBusList是成功的”,这似乎是onComplete方法中的RuntimeException丢失了!我如何在最后一个来电中抓住它?处理此类未来依赖关系的更好实践是什么?

更新:根据@dk14的解释,选择将中间方法转换为返回未来,基本上所有方法都返回某种未来,而不是一个简单的异常。

object ETLController {

  private def getBusinessListFromModules(modulePaths: Iterable[File]) : Future[String] = {
    implicit val ec = ExecutionContext.global
    println("Inside getBusinessListFromModules..")
    Future {
      Thread.sleep(2000)
      throw new java.lang.RuntimeException("failed to get businesses")
      "ok"
    }
  }

  private def callGetBusList(modulePaths: Iterable[File]) : Future[String] = {

    implicit val ec = ExecutionContext.global
    val etlF = getBusinessListFromModules(modulePaths)    

    etlF map { itr => 
        println("Future getBusinessListFromModules success: "+ itr)
        throw new java.lang.RuntimeException("RTE from callGetBusList")
      } recover {
      case t: Throwable => {
        println("Future callGetBusList throws an error: " + t.getMessage)
        throw t
      }
    }    
  }

  def main(args: Array[String]) {

    println("Inside Future Test..")
    implicit val ec = ExecutionContext.global
    val modulePaths = Iterable(new File("mdrqaint/MDR/QAINT/QAINTX"))
    val fut = callGetBusList(modulePaths)
    fut.map { str =>
        println("successful  future! "+ str)
    }.recover{
      case t: Throwable => println("Failed  future: "+ t.getMessage)
    }
    println("Active threads: " +Thread.activeCount())
    sys.allThreads().foreach(t => t.join())

  }    
}

共有1个答案

蒋栋
2023-03-14

1)期货正在急切地开火,它们不是参考透明的。所引用问题的答案还包含有关Future内部行为的一些见解,因此我想在此处跳过它。

为了以更可预测的方式管理关于执行池/队列/线程的副作用,您可以考虑scalaz/monix/fs2 < code > Task 或iterate/scalaz/cats < code > Eval (更抽象的惰性评估,用于同步)< code>Cont (continuations是对订阅的抽象)作为替代。所有这些都是引用透明的,并且“按需”开始执行。

2) 最好的方法是你不喜欢的方法:不要将异常抛出未来的上下文。

您还可以考虑使用< code>flatMap来避免< code>Future[Future[T]]

3)双包装期货直接a-la 期货(Future(...))并不能改变什么。您的方法在< code>val etlF = g上执行...(在同一个线程中),不管它返回什么。< code>Future("ok")的内容(lambda)在不同的线程上急切地执行(有“小的”不可预测的延迟),但[执行任务正在提交到池]仍在< code > getBusinessListFromModules 内。

一种解决方法(不推荐)是< code > val etlF = Future(getbusinesslistfromodules(...)).flatMap(identity)返回一个future,包装直接来自< code > getBusinessListFromModules 和间接来自< code > getBusinessListFromModules 的内部< code>Future的任何异常。

最好重构getBusinessListFromModules本身,但是,对于您的方法可能遇到的不同类型的麻烦(验证,同步与异步等),也会引入不同的异常类型。

有很多方法可以混合异步和同步异常处理,但是实际上很难分析和预测这种混合行为(你可能已经注意到了)。代码变得丑陋。

 类似资料:
  • 我希望下面的代码在其中一个方法<code>callfuture1()</code>或<code>callfuture2()</code>抛出异常时返回自定义消息。我的理解是,如果任何一个未来失败,都将是失败的未来。 但是,当 引发异常时。 不执行。相反,我看到调用堆栈停止在 中的代码行,其中发生了异常并返回了标准内部错误。为什么会这样? ====更新==== 我从响应中看到,潜在的问题是异常被抛出

  • java completableFuture是否有thenComose方法,该方法具有CompletionStage值类型的param来处理异常?像这样的方法: 我想处理结果和异常并使用函数A转换结果,返回完整的Future,就像: 函数A将调用RPC。它是一个异步函数。 复杂uture.handle

  • 我正在寻找一种将任意长度的期货列表转换为期货列表的方法。我使用的是Playframework,所以最终,我真正想要的是一个<code>未来〔结果〕,但为了让事情更简单,让我们说<code>将来〔List[Int]]通常的方法是使用<code>Future.sequence(…) 例如,执行以下操作不起作用: 我希望能够将1和3从那里拉出来,而不是只得到异常。我尝试将来使用<code>。折叠,但这显

  • 我想在我的play scala Web应用程序中进行错误处理。 我的应用程序与数据库对话以获取一些行,它遵循以下流程。 < li >首先调用数据库以获取一些数据 < li >使用第一次调用中的数据从数据库中提取其他数据 < li >使用从最近两次db调用中收到的数据形成响应。 下面是我的伪代码。 以上理解中的每一个方法都返回一个未来,这些方法的签名如下。 在以下情况下,我该如何进行错误/故障处理

  • 我有一些简单的枚举名:失败/成功的结果 我有一个函数,它什么也没有得到,但返回未来的结果, 我不喜欢我的解决方案,它看起来很凌乱,有更简单的吗? 返回 我想在这里做的是,如果返回None,那么func应该返回Future[Fail],否则调用db,这也返回一个case类的未来,并基于响应返回Fail/Success。 但我正在寻找一种超级时尚的斯卡拉方式,让它看起来不错:) 谢谢

  • 我正在阅读Scala Cookbook(http://shop.oreilly.com/product/0636920026914.do) 有一个与未来使用相关的例子,涉及理解。 到目前为止,我对理解的理解是,当与一个集合一起使用时,它会产生另一个相同类型的集合。例如,如果每个< code>futureX的类型为< code>Future[Int],则以下内容也应为< code>Future[In