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

bd1hkmkf  于 2022-12-18  发布在  Scala
关注(0)|答案(2)|浏览(197)

我想在我的play scala web应用程序中做错误处理。
我的应用程序与数据库对话以获取一些行,它遵循以下流程。
1.第一次调用db以获取一些数据
1.使用第一次调用中的数据从数据库中获取其他数据
1.使用从最后两次数据库调用接收到的数据形成响应。
下面是我的伪代码。

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
  }

上述理解中的每个方法都返回一个future,这些方法的签名如下。

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

在以下情况下,我应该如何处理错误/故障

  • 当callFuture 1无法从数据库提取数据时。我希望返回包含错误消息的相应错误响应。由于callFuture 2仅在callFuture 1之后执行。如果callFuture 1失败/出错,我不希望执行callFuture 2,并希望立即返回错误消息。(callFuture 2和callFuture 3的情况相同)
  • 编辑-
    我尝试从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
  }


运行时错误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
2w2cym1i

2w2cym1i1#

您可以使用Future.recoverWith函数来定制Future失败时的异常。

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
如果您不希望细粒度控制根据失败的Future中的Throwable设置不同的错误消息(如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))
  }
}


你可以这样使用:

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中的错误消息。
您没有指定要如何处理错误消息。例如,如果要使用错误消息创建不同的Response,则可以使用recoverWithrecover

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

xuo3flqw2#

假设future1future2future3抛出Throwable异常,分别命名为Future1ExceptionFuture2ExceptionFuture3Exception,那么可以从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(...)
  }
}

根据文件Future.recover
创建一个新的future,它将处理该future可能包含的任何匹配的throwable。

相关问题