簡體   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