[英]Error handling Scala : Future For Comprehension
I want to do error handling in my play scala web application. 我想在我的Play scala Web应用程序中进行错误处理。
My application talks to the data base to fetch some rows, it follows following flow. 我的应用程序与数据库进行对话以获取一些行,它遵循以下流程。
Below is my pseudocode. 下面是我的伪代码。
def getResponse(name: String)
(implicit ctxt: ExecutionContext): Future[Response] = {
for {
future1 <- callFuture1(name)
future2 <- callFuture2(future1.data)
future3 <- callFuture3(future1.data, future2.data)
} yield future3
}
Every method in the comprehension above returns a future, the signature of these methods are as below. 上面的理解中的每个方法都返回一个未来,这些方法的签名如下。
private def callFuture1(name: String)
(implicit ctxt: ExecutionContext): Future[SomeType1] {...}
private def callFuture2(keywords: List[String])
(implicit ctxt: ExecutionContext): Future[SomeType2] {...}
private def callFuture3(data: List[SomeType3], counts: List[Int])
(implicit ctxt: ExecutionContext): Future[Response] {...}
How shall I do error/failure handling, in the following situation 在下列情况下,如何处理错误/故障处理
--edit-- - 编辑 -
I am trying to return an appropriate Error Response from getResponse() method, when either of the callFuture fails and not proceed to subsequent futureCalls. 我试图从getResponse()方法返回一个适当的错误响应,当callFuture中的任何一个失败并且不进入后续的futureCalls时。
I tried the following, based on Peter Neyens answer, but gave me an runtime error.. 我根据Peter Neyens的回答尝试了以下内容,但是给了我一个运行时错误..
def getResponse(name: String)
(implicit ctxt: ExecutionContext): Future[Response] = {
for {
future1 <- callFuture1(name) recoverWith {
case e:Exception => return Future{Response(Nil,Nil,e.getMessage)}
}
future2 <- callFuture2(future1.data)
future3 <- callFuture3(future1.data, future2.data)
} yield future3
}
Runtime error i get 我得到运行时错误
ERROR] [08/31/2015 02:09:45.011] [play-akka.actor.default-dispatcher-3] [ActorSystem(play)] Uncaught error from thread [play-akka.actor.default-dispatcher-3] (scala.runtime.NonLocalReturnControl)
[error] a.a.ActorSystemImpl - Uncaught error from thread [play-akka.actor.default-dispatcher-3]
scala.runtime.NonLocalReturnControl: null
You could use the Future.recoverWith
function, to customize the exception if the Future
failed. 如果
Future
失败,您可以使用Future.recoverWith
函数来自定义异常。
val failed = Future.failed(new Exception("boom"))
failed recoverWith {
case e: Exception => Future.failed(new Exception("A prettier error message", e)
}
This will result in a slightly uglier for comprehension : 这将导致理解力稍微丑陋:
for {
future1 <- callFuture1(name) recoverWith {
case npe: NullPointerException =>
Future.failed(new Exception("how did this happen in Scala ?", npe))
case e: IllegalArgumentException =>
Future.failed(new Exception("better watch what you give me", e))
case t: Throwable =>
Future.failed(new Exception("pretty message A", t))
}
future2 <- callFuture2(future1.data) recoverWith {
case e: Exception => Future.failed(new Exception("pretty message B", e))
}
future3 <- callFuture3(future1.data, future2.data) recoverWith {
case e: Exception => Future.failed(new Exception("pretty message C", e))
}
} yield future3
Note that you could also define your own exceptions to use instead of Exception
, if you want to add more information than just an error message. 请注意,如果要添加更多信息而不仅仅是错误消息,还可以定义自己的异常以代替
Exception
。
If you don't want fine grained control to set a different error message depending on the Throwable
in the failed Future
(like with callFuture1
), you could enrich Future
using an implicit class to set a custom error message somewhat simpler: 如果您不希望细粒度控制根据失败的
Future
的Throwable
设置不同的错误消息(例如使用callFuture1
),您可以使用隐式类来丰富Future
以设置更简单的自定义错误消息:
implicit class ErrorMessageFuture[A](val future: Future[A]) extends AnyVal {
def errorMsg(error: String): Future[A] = future.recoverWith {
case t: Throwable => Future.failed(new Exception(error, t))
}
}
Which you could use like : 您可以使用哪个:
for {
future1 <- callFuture1(name) errorMsg "pretty A"
future2 <- callFuture2(future1.data) errorMsg "pretty B"
future3 <- callFuture3(future1.data, future2.data) errorMsg "pretty C"
} yield future3
In both cases, using errorMsg
or recoverWith
directly, you still rely on Future
, so if a Future
fails the following Futures
will not be executed and you can directly use the error message inside the failed Future
. 在这两种情况下,直接使用
errorMsg
或recoverWith
,您仍然依赖于Future
,因此如果Future
失败,则不会执行以下Futures
,您可以直接在失败的Future
使用错误消息。
You didn't specify how you would like to handle the error messages. 您没有指定如何处理错误消息。 If for example you want to use the error message to create a different
Response
you could use recoverWith
or recover
. 例如,如果您要使用错误消息创建不同的
Response
,则可以使用recoverWith
或recover
。
future3 recover { case e: Exception =>
val errorMsg = e.getMessage
InternalServerError(errorMsg)
}
Say future1
, future2
and future3
throw Throwable
exceptions named Future1Exception
, Future2Exception
and Future3Exception
, respectively. 比如
future1
, future2
和future3
抛出名为Future1Exception
, Future2Exception
和Future3Exception
Throwable
异常。 Then you can return appropriate error Response
from getResponse()
method as follows: 然后,您可以从
getResponse()
方法返回相应的错误Response
,如下所示:
def getResponse(name: String)
(implicit ctxt: ExecutionContext): Future[Response] = {
(for {
future1 <- callFuture1(name)
future2 <- callFuture2(future1.data)
future3 <- callFuture3(future1.data, future2.data)
} yield future3).recover {
case e: Future1Exception =>
// build appropriate Response(...)
case e: Future2Exception =>
// build appropriate Response(...)
case e: Future3Exception =>
// build appropriate Response(...)
}
}
According to documentation Future.recover
根据
Future.recover
文档
Creates a new future that will handle any matching throwable that this future might contain.
创建一个新的未来,它将处理此未来可能包含的任何匹配throwable。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.