[英]Scala Future error handling with Either
我正在编写API的包装器,我想对应用程序问题进行错误处理。 每个请求都返回一个Future,所以为了做到这一点,我看到了两个选项:使用Future[Either]
或使用exception来立即失败。
以下是两种情况的代码段, response
是HTTP请求返回的未来:
def handleRequestEither: Future[Either[String, String]] = {
response.map {
case "good_string" => Right("Success")
case _ => Left("Failed")
}
}
def handleRequest: Future[String] = {
response.map {
case "good_string" => "Success"
case _ => throw new Exception("Failed")
}
}
以下是在两种情况下获得结果的片段:
handleRequestEither.onComplete {
case Success(res) =>
res match {
case Right(rightRes) => println(s"Success $res")
case Left(leftRes) => println(s"Failure $res")
}
case Failure(ex) =>
println(s"Failure $ex")
}
handleRequest.onComplete {
case Success(res) => println(s"Success $res")
case Failure(ex) => println(s"Failure $ex")
}
我不喜欢使用异常,但是使用Future[Either]
使之后获得响应变得更加冗长,如果我想将结果映射到另一个对象,它会变得更加复杂。 这是要走的路,还是有更好的选择?
让我解释一下Erik Meijer并考虑下表:
然后考虑一下语言结构的这两个特性: arity (它是聚合一个还是多个项?)和模式 (阻塞读操作时同步,直到准备好或异步时)。
所有这些都意味着Try
构造和块管理同步生成结果的块的成功或失败。 您可以控制资源是否提供正确答案而不会遇到问题(例外情况描述的问题)。
另一方面, Future
是一种异步Try
。 这意味着它在没有发现任何问题(例外)然后通知其订户时成功完成。 因此, 我不认为你应该有一个未来Either
在这种情况下 ,这是你第二次handleRequest
实现是利用期货的正确方法。
最后,如果扰乱你的是抛出异常,你可以遵循Promises
的方法:
def handleRequest: Future[String] = {
val p = Promise[String]
response.map {
case "good_string" => p.success("Success")
case _ => p.failure(new Exception("Failed"))
}
p.future
}
要么:
case class Reason(msg: String) extends Exception
def handleRequest: Future[String] = {
val p = Promise[String]
response.map {
case "good_string" => p.success("Success")
case _ => p.failure(Reason("Invalid response"))
}
p.future
}
我宁愿使用你的第二种方法。
它适用于scalaz增强版的Either
: \\/
它可以将任何monad和\\/
组合转换为单个monad。 因此,对于scala.concurent.Future
使用scalaz实例,您可以实现所需的混合。 如果你愿意,你可以进一步使用monad变形金刚。 如果您有兴趣,请阅读这个美丽的博客。
这里没有美化,但为您使用scalaz 7.1示例:
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scalaz._
import scalaz.std.scalaFuture._
import EitherT._
import scala.concurrent.ExecutionContext.Implicits.global
object EitherFuture {
type ETFS[X] = EitherT[Future, String, X]
val IntResponse = "result (\\d+)".r
def parse(response: Future[String]) =
eitherT(response map {
case IntResponse(num) ⇒ \/-(num.toInt)
case _ ⇒ -\/("bad response")
})
def divideBy2(x: Validation[String, Int]) =
x.ensure("non divisible by 2")(_ % 2 == 0).map(_ / 2)
def handleResponse(response: Future[String]) = for {
num ← parse(response).validationed(divideBy2)
} yield s"half is $num"
def main(args: Array[String]) {
Map(
'good → "result 10",
'proper → "result 11",
'bad → "bad_string"
) foreach { case (key, str) ⇒
val response = Future(str)
val handled = handleResponse(response)
val result = Await.result(handled.run, Duration.Inf)
println(s"for $key response we have $result")
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.