错误处理 Scala:理解的未来

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32292750/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 07:31:54  来源:igfitidea点击:

Error handling Scala : Future For Comprehension

scalaerror-handlingplayframeworkfuture

提问by konquestor

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
  2. Use the data in first call to fetch other data from db
  3. Form a response using the data received from last two db calls.
  1. 首先调用 db 来获取一些数据
  2. 使用第一次调用中的数据从数据库中获取其他数据
  3. 使用从最近两次 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. 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)
  • 当 callFuture1 无法从数据库中获取数据时。我想返回带有错误消息的适当错误响应。由于 callFuture2 仅在 callFuture1 之后执行。如果 callFuture1 失败/出错,我不想执行 callFuture2 并且想立即返回错误消息。(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

回答by Peter Neyens

You could use the Future.recoverWithfunction, to customize the exception if the Futurefailed.

Future.recoverWith如果Future失败,您可以使用该函数来自定义异常。

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 Throwablein the failed Future(like with callFuture1), you could enrich Futureusing an implicit class to set a custom error message somewhat simpler:

如果您不希望细粒度控制根据Throwable失败中的设置不同的错误消息Future(如 with 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 errorMsgor recoverWithdirectly, you still rely on Future, so if a Futurefails the following Futureswill not be executed and you can directly use the error message inside the failed Future.

在这两种情况下,使用errorMsgrecoverWith直接,您仍然依赖Future,因此如果 aFuture失败,Futures则不会执行以下操作,您可以直接使用 failed 中的错误消息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 Responseyou could use recoverWithor recover.

您没有指定您希望如何处理错误消息。例如,如果您想使用错误消息来创建一个不同的,Response您可以使用recoverWithrecover

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

回答by Mario Galic

Say future1, future2and future3throw Throwableexceptions named Future1Exception, Future2Exceptionand Future3Exception, respectively. Then you can return appropriate error Responsefrom getResponse()method as follows:

future1,future2并分别future3抛出Throwable名为Future1Exception,Future2Exception和 的异常Future3Exception。然后您可以ResponsegetResponse()方法返回适当的错误,如下所示:

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。