我正在编写API的包装器,我想对应用程序问题进行错误处理。每个请求都返回一个Future,所以为了做到这一点,我看到了两个选项:使用Future[Either]
或使用例外来立即失败。
以下是两种情况的代码段,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]
会使得后续获得响应更加冗长,如果我想将结果映射到另一个对象,则会变得更加复杂。这是要走的路,还是有更好的选择?
答案 0 :(得分:4)
让我解释Erik Meijer并考虑下表:
然后考虑一下语言构造的这两个特性: arity (它是聚合一个还是多个项?)和模式(阻塞读取操作时同步,直到准备好或异步当没有)。
所有这些都暗示Try
构造和块管理同步生成结果的块的成功或失败。您可以控制您的资源是否提供正确的答案而不会遇到问题(例外情况描述的问题)。
另一方面,Future
是一种异步Try
。这意味着它在没有发现任何问题(例外)然后通知其订户时成功完成。因此,我认为你不应该在这种情况下拥有Either
,这是你的第二个handleRequest
实施是使用期货的正确方法。 / p>
最后,如果扰乱你的是抛出异常,你可以按照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
}
我宁愿使用你的第二种方法。
答案 1 :(得分:0)
您可以使用特殊类型:EitherT中的scalaz library。
适用于Either
:\/
它可以将任何monad和\/
的组合转换为单个monad。因此,使用scala.concurent.Future
的scalaz实例可以实现所需的混合。如果你愿意,你可以进一步使用monad变形金刚。 Read this beautiful blog if you're interested.
这里没有经过美化,但是为你准备了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")
}
}
}