代码之家  ›  专栏  ›  技术社区  ›  konquestor

错误处理Scala:理解的未来

  •  22
  • konquestor  · 技术社区  · 9 年前

    我想在playscala web应用程序中进行错误处理。

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

    1. 第一次调用数据库以获取一些数据
    2. 使用第一次调用中的数据从数据库中获取其他数据
    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
      }
    

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

    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无法从数据库中获取数据时。我想回来 带有错误消息的适当错误响应。自调用Future2以来 只在callFuture1之后执行。我不想执行 如果callFuture1失败/错误并希望返回,则调用Future2 立即显示错误消息。(调用Future2和 呼叫未来3)

    --编辑--

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

    我根据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 回复  |  直到 9 年前
        1
  •  37
  •   Peter Neyens    9 年前

    你可以使用 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 在失败的 将来 (与 callFuture1 ),你可以丰富 将来 使用隐式类设置自定义错误消息稍微简单一些:

    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 直接来说,你仍然依赖 将来 ,所以如果 将来 无法执行以下操作 Futures 将不会执行,您可以直接在失败的 将来 .

    您没有指定如何处理错误消息。例如,如果希望使用错误消息创建不同的 Response 你可以使用 恢复方式 recover .

    future3 recover { case e: Exception =>
      val errorMsg = e.getMessage
      InternalServerError(errorMsg)
    }
    
        2
  •  6
  •   Mario Galic    7 年前

    future1 , future2 future3 Throwable 名为的异常 Future1Exception , Future2Exception Future3Exception 分别地然后可以返回适当的错误 Response 从…起 getResponse() 方法如下:

    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

    创建一个新的未来,它将处理 未来可能包含。