繁体   English   中英

通过 Scala 期货链传播错误

[英]Propagate errors through a chain of Scala futures

考虑一系列期货,每个期货返回 Either[Status, Resp]。 您将如何通过使用 Future 而不是 Either 的 for comprehension 传播错误状态代码?

下面的代码不起作用,因为解析异常没有被最后一个未来的恢复捕获

用例是 Scala Play ActionRefiners,它返回 Future[Either[Status, TRequest[A]]]。

  def parseId(id: String):Future[Int] = {
    Future.successful(Integer.parseInt(id))
  }

  def getItem(id: Int)(implicit ec: ExecutionContext): Future[Either[Status, String]] =
   Future(Some("dummy res from db " + id)).transformWith {
    case Success(opt) => opt match {
      case Some(item) => Future.successful(Right(item))
      case _ => Future.successful(Left(NotFound))
    }
    case Failure(_) => Future.successful(Left(InternalServerError))
  }

  (for {
    id <- parseId("bad request")
    resp <- getItem(id)
  } yield resp).recover {
    case _:NumberFormatException => Left(BadRequest)
  }

我可以将 the.recover 移动到 parseId,但这使得 for 理解非常难看 - 必须在中间处理 Either[Status, id]

  def parseId(id: String):Future[Either[Status, Int]] = {
    Future.successful(Right(Integer.parseInt(id))).recover {
      case _:NumberFormatException => Left(BadRequest)
    }
  }

你的异常没有被捕获,因为你没有把它扔进FutureFuture.successful立即满足你给它的表达式的结果,如果它抛出异常,它会在当前线程上执行。

尝试删除.successfulFuture(id.toInt)会做你想做的。

此外,我建议摆脱所有Either s:这些被高度高估/过度使用,特别是在Future的上下文中(无论如何已经将它们的结果包装到Try中),并且只是使代码更复杂且可读性更差而不提供很多好处。

    case class FailureReason(status: Status) 
      extends Exception(status.toString)

    def notFound() = throw FailureReason(NotFound)
    def internalError() = throw FailureReason(InternalError)
    def badRequest() = throw FailureReason(BadRequest)
    

    def parseId(id: String):Future[Int] = Future(id.toInt)
    def getItem(id: Int): Future[String] = Future(Some("dummy"))
       .map { _.getOrElse(notFound) }
       .recover { _ => internalError }

    // this is the same as your for-comprehension, just looking less ugly imo :) 
    parseId("foo").flatMap(getItem).recover { 
      case _: NumberFormatException => badRequest()
    }
    // if you still want `Either` in the end for some reason: 
    .map(Right.apply[Status, String])
    .recover { 
       case _: NumberFormatException => Left(BadRequest) // no need for the first recover above if you do this
       case FailureReason(status) => Left(status)
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM