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

时间:2015-08-30 03:02:30

标签: scala error-handling playframework future

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

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

  1. 首先调用db来获取一些数据
  2. 使用第一次调用中的数据从db
  3. 获取其他数据
  4. 使用从最后两次数据库调用收到的数据形成响应。
  5. 下面是我的伪代码。

     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
      }
    

    上面的理解中的每个方法都会返回一个未来,这些方法的签名如下所示。

    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] {...}
    

    在下列情况下,我该如何处理错误/失败?

    • 当callFuture1无法从数据库中获取数据时。我想回来 带有错误消息的适当错误响应。自callFuture2起 只在callFuture1之后执行。我不想执行 如果callFuture1失败/错误并且想要返回,则调用callFuture2 立即出错信息。 (callFuture2和 callFuture3)

    - 编辑 -

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

    我根据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
      }
    

    我得到运行时错误

    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
    

2 个答案:

答案 0 :(得分:26)

如果Future失败,您可以使用Future.recoverWith函数自定义例外。

val failed = Future.failed(new Exception("boom"))
failed recoverWith {
  case e: Exception => Future.failed(new Exception("A prettier error message", e)
}

这将导致理解力稍微丑陋:

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

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

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

在这两种情况下,直接使用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 errorMsg,您仍然依赖recoverWith,因此如果Future失败,则以下Future将不会已执行,您可以直接在失败的Futures内使用错误消息。

您没有指定处理错误消息的方式。例如,如果您要使用错误消息创建其他Future,则可以使用ResponserecoverWith

recover

答案 1 :(得分:3)

分别说出future1future2future3抛出ThrowableFuture1ExceptionFuture2Exception的{​​{1}}个例外情况。然后,您可以从Future3Exception方法返回相应的错误Response,如下所示:

getResponse()

根据文件Future.recover

  

创建一个新的未来,将处理任何匹配的throwable    未来可能包含。