scala异常处理与一系列相关的未来和非未来方法调用

scala异常处理与一系列相关的未来和非未来方法调用,scala,future,Scala,Future,我有一个方法可以返回Future-成功或失败,甚至可以抛出异常。我可以通过在整个方法上放置try-catch块并始终返回Future来避免这种情况,但我现在想避免这种情况。我在调用这种方法时没有什么问题: 1) 在调用方代码中,如果我使用map,我期望执行一个方法,并期望未来或异常,我尝试以以下方式处理该异常: object ETLCoordinator { private def getBusinessListFromModules(modulePaths: Iterable

我有一个方法可以返回
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)
    }
  }    
}
输出:(不执行上面的恢复或捕获块)

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

Inside Future Test..
Inside getBusinessListFromModules..
Exception in fut1: failed to get businesses
为什么会这样?我认为未来的执行会在调用它的一些方法时发生,如map、flatmap、onSuccess、onComplete等。在本例中,对
map
的调用已经在Try块中

2) 定义和调用这些方法的更好方法是什么?调用程序中的Try/catch块还是方法本身中的Try/catch?或者其他任何方式。我尝试在Future中包装调用方法,以便在调用者中获得Future[Future[String]]。我能够避免所有的尝试捕捉

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)
    }

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

我甚至尝试对未来的电话进行双重包装:

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是成功的”,这似乎是
RuntimeException
内部
onComplete
方法丢失了!如何在最后一个来电者中捕捉到它?处理此类未来依赖关系的更好实践是什么

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

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) 期货市场正如火如荼地进行着,而且它们也在继续。 参考问题的答案还包含一些关于
未来
内部行为的见解,因此我想跳过这里

<>为了更好地管理执行池/队列/线程的副作用,您可以考虑//>代码>任务< /代码>或/CASTAZI/CATIOS>代码> EVA/COD>(更抽象的懒惰评估,并打算用于同步填充)+(继续在订阅上抽象)作为备选方案。所有这些都是引用透明的,并且“按需”延迟地开始执行

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

您也可以考虑<代码>平面图< /> >以避免<代码>未来[未来[t] ] < /代码>

3) 直接双重包装期货a-la
Future(Future(…)
不会改变任何东西。您的方法将在
val etlF=g..
(在同一线程中)上执行,无论它返回什么
Future(“ok”)
的内容(lambda)在不同的线程上急切地执行(具有“小的”不可预测的延迟),但[执行任务正在提交到池]仍然在
getBusinessListFromModules

一种解决方法(并非真正推荐)是
val etlF=Future(getBusinessListFromModules(…).flatMap(identity)
,它将返回一个未来,包装直接来自
getBusinessListFromModules
和间接来自
getBusinessListFromModules
内部
的任何异常

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

另外,有多种方法可以混合使用异步和同步异常处理,但实际上很难分析和预测这种混合行为(您可能已经注意到)。代码变得难看。

1)期货市场正在火热地启动,它们也在继续。 参考问题的答案还包含一些关于
未来
内部行为的见解,因此我想跳过这里

<>为了更好地管理执行池/队列/线程的副作用,您可以考虑//>代码>任务< /代码>或/CASTAZI/CATIOS>代码> EVA/COD>(更抽象的懒惰评估,并打算用于同步填充)+(继续在订阅上抽象)作为备选方案。所有这些都是引用透明的,并且“按需”延迟地开始执行

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

您也可以考虑<代码>平面图< /> >以避免<代码>未来[未来[t] ] < /代码>

3) 直接双重包装期货a-la
Future(Future(…)
不会改变任何东西。您的方法将在
val etlF=g..
(在同一线程中)上执行,无论它返回什么
Future(“ok”)
的内容(lambda)在不同的线程上急切地执行(具有“小的”不可预测的延迟),但[执行任务正在提交到池]仍然在
getBusinessListFromModules

一种解决方法(并非真正推荐)是
val etlF=Future(getBusinessListFromModules(…).flatMap(identity)
,它将返回一个未来,包装直接来自
getBusinessListFromModules
和间接来自
getBusinessListFromModules
内部
的任何异常

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


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

因此,一个应该返回某种未来的方法,甚至在有机会编写未来之前就抛出了一个异常
Future getBusinessListFromModules success: ok
java.lang.RuntimeException: RTE from callGetBusList
    at 
successful inner future! callGetBusList was a success
successful outer future!
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())

  }    
}