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