简体   繁体   English

处理Scala时出错:理解的未来

[英]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. 我的应用程序与数据库进行对话以获取一些行,它遵循以下流程。

  1. First call to db to fetch some data 首先调用db来获取一些数据
  2. Use the data in first call to fetch other data from db 使用第一次调用中的数据从db获取其他数据
  3. Form a response using the data received from last two db calls. 使用从最后两个db调用收到的数据形成响应。

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 在下列情况下,如何处理错误/故障处理

  • When callFuture1 fails to fetch data from database. 当callFuture1无法从数据库中获取数据时。 I want to return a appropriate error response with error message. 我想返回一个错误消息的相应错误响应。 Since callFuture2 only gets executed after callFuture1. 因为callFuture2只在callFuture1之后执行。 I dont want to execute callFuture2 if callFuture1 has failed/erred and would want to return error message immediately. 如果callFuture1失败/错误并且想要立即返回错误消息,我不想执行callFuture2。 (Same thing for callFuture2 and callFuture3) (callFuture2和callFuture3也是一样)

--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: 如果您不希望细粒度控制根据失败的FutureThrowable设置不同的错误消息(例如使用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 . 在这两种情况下,直接使用errorMsgrecoverWith ,您仍然依赖于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 ,则可以使用recoverWithrecover

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. 比如future1future2future3抛出名为Future1ExceptionFuture2ExceptionFuture3Exception 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM