[英]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)
}
}
你的异常没有被捕获,因为你没有把它扔进Future
: Future.successful
立即满足你给它的表达式的结果,如果它抛出异常,它会在当前线程上执行。
尝试删除.successful
: Future(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.