scala - 处理Scala : Future For Comprehension时出错

标签 scala error-handling playframework future

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

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

  • 第一次调用db以获取一些数据
  • 在第一次调用中使用数据从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
      }
    

    上面理解中的每个方法都会返回一个 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] {...}
    

    在以下情况下应如何处理错误/故障
  • 当callFuture1无法从数据库获取数据时。我想回来
    带有错误消息的适当错误响应。从callFuture2开始
    仅在callFuture1之后执行。我不想执行
    如果callFuture1失败/出错并想返回,则调用callFuture2
    立即出现错误消息。 (对于callFuture2和
    callFuture3)

  • - 编辑 -

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

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

    最佳答案

    如果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

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

    关于scala - 处理Scala : Future For Comprehension时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32292750/

    相关文章:

    Java:写入 Scala 输出流

    java - Apache Spark 启动时出错

    php - 谁能用 PHP -> $a{0} = "value"解释这个数组声明

    postgresql - Ebean 中的 Heroku Postgres bytea

    Scala 并行赋值仅在声明中

    java - 通过反射修改不可变的scala类字段

    python - 什么是 'local variable referenced before assignment'

    python - Python错误 “ZeroDivisionError: float division by zero”

    playframework - playframework 中的对话,或者更确切地说选项卡状态与 session 状态

    java - 将商品添加到购物车时,playframework 中的功能测试失败