错误处理Scala:理解的未来 [英] Error handling Scala : Future For Comprehension

查看:110
本文介绍了错误处理Scala:理解的未来的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我的play scala Web应用程序中进行错误处理.

I want to do error handling in my play scala web application.

我的应用程序与数据库进行对话以获取一些行,它遵循以下流程.

My application talks to the data base to fetch some rows, it follows following flow.

  1. 第一次调用db来获取一些数据
  2. 在首次调用中使用数据从数据库中获取其他数据
  3. 使用从最近两个数据库调用中接收到的数据来形成响应.

下面是我的伪代码.

 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

  • 当callFuture1无法从数据库获取数据时.我想回来 带有错误消息的适当的错误响应.从callFuture2开始 仅在callFuture1之后执行.我不想执行 如果callFuture1失败/出错并想返回,则调用callFuture2 立即出现错误消息. (对于callFuture2和 callFuture3)
  • When callFuture1 fails to fetch data from database. I want to return a appropriate error response with error message. Since callFuture2 only gets executed after callFuture1. I dont want to execute callFuture2 if callFuture1 has failed/erred and would want to return error message immediately. (Same thing for callFuture2 and callFuture3)

-编辑-

当任何一个callFuture失败并且不继续进行后续的futureCalls时,我试图从getResponse()方法返回适当的错误响应.

I am trying to return an appropriate Error Response from getResponse() method, when either of the callFuture fails and not proceed to subsequent futureCalls.

基于Peter Neyens的回答,我尝试了以下操作,但给了我运行时错误.

I tried the following, based on Peter Neyens answer, but gave me an runtime error..

 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
  }

我发现运行时错误

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.

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

请注意,如果您想添加除错误消息之外的更多信息,还可以定义自己的异常来代替Exception.

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.

如果您不希望细粒度的控件根据失败的Future中的Throwable设置不同的错误消息(如callFuture1一样),则可以使用隐式类来丰富Future来进行设置自定义错误消息稍微简单一些:

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:

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

您可以这样使用:

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

在两种情况下,直接使用errorMsgrecoverWith,您仍然依赖Future,因此,如果Future失败,将不执行以下Futures,您可以直接使用内部的错误消息失败的Future.

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.

您没有指定要如何处理错误消息.例如,如果要使用错误消息创建其他Response,则可以使用recoverWithrecover.

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.

future3 recover { case e: Exception =>
  val errorMsg = e.getMessage
  InternalServerError(errorMsg)
}

这篇关于错误处理Scala:理解的未来的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆