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

kon*_*tor 18 error-handling scala future playframework

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

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

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

下面是我的伪代码.

 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
  }
Run Code Online (Sandbox Code Playgroud)

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

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] {...}
Run Code Online (Sandbox Code Playgroud)

在下列情况下,如何处理错误/故障处理

  • 当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
  }
Run Code Online (Sandbox Code Playgroud)

我得到运行时错误

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
Run Code Online (Sandbox Code Playgroud)

Pet*_*ens 27

您可以使用该Future.recoverWith函数,在Future失败时自定义异常.

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

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

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
Run Code Online (Sandbox Code Playgroud)

请注意Exception,如果要添加更多信息而不仅仅是错误消息,还可以定义自己的例外以代替使用.

如果您不希望细粒度控制根据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))
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用哪个:

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
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,使用errorMsgrecoverWith直接使用,您仍然依赖Future,因此如果Future失败,Futures则不会执行以下操作,您可以直接在失败中使用错误消息Future.

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

future3 recover { case e: Exception =>
  val errorMsg = e.getMessage
  InternalServerError(errorMsg)
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*lic 5

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(...)
  }
}
Run Code Online (Sandbox Code Playgroud)

根据文档 Future.recover

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