如何不抛出异常?
How to NOT throw an exception?
我有以下 Slick 代码,它给了一个客户 ID returns(如果存在的话)。如果出现问题(例如连接丢失),Failure
子句将抛出异常:
def read (id: Int): Future[Option[Customer]] = {
val db = // ....
val customers = TableQuery[CustomerDB]
val action = customers.filter(_.id === id).result
val future = db.run(action.asTry)
future.map{
case Success(s) =>
if (s.length>0)
Some(s(0))
else
None
case Failure(f) => throw new Exception (f.getMessage)
}
}
现在,我的理解是,在 Scala 中应该使用 Try,而不是使用 try/catch/finally 异常。此外,不应抛出任何异常。但是如果没有抛出异常,如何通知上层出现了问题呢?
报告错误的正确方法是使用 Either。
trait Error
case class NotFound(id: Int) extends Error
case class QueryFailed(msg: String) extends Error
def read (id: Int): Future[Either[Error, Customer]] = {
val db = // ....
val customers = TableQuery[CustomerDB]
val action = customers.filter(_.id === id).result
val future = db.run(action.asTry)
future.map{
case Success(s) =>
if (s.length>0)
Right(s(0))
else
Left(NotFound(id))
case Failure(f) => Left(QueryFailed(f.getMessage))
}
}
Future
本身确实已经有 Try 里面了。所以,我会说你只需要展平(而且你的代码有点复杂,我简化了):
future.flatMap {
case Success(s) => Future.successful(s.headOption)
case Failure(f) => Future.failed(f)
}
结果 Future
当处于 failed
状态时通知调用者执行失败(包含原始异常)。否则,成功。
好的,一般来说,您可以使用 Future.successful 或 Future.failed(msg: String) 到 "signal" 上层(也称为调用方法),您是否获得了值。
更好的方法
然而,一个好的方法是在 Future 上使用 .recoverWith{}
以防失败。
例如:
def getUserFromCloud (userId: String): Future[String] = Future{
cloudProviderApi.getUsername(userId)
}.recoverWith{
Future.failed(s"$userId does not exist.")
}
调用方式呢?
好吧,你只需将成功映射到下划线并使用 recover 处理错误:
getUserFromCloud("test").map(_ => {
//In case of success
}).recover{
//In case of failure, like return BadRequest.
}
有关 recover
和 recoverWith
的更多信息,如果您有兴趣:
我有以下 Slick 代码,它给了一个客户 ID returns(如果存在的话)。如果出现问题(例如连接丢失),Failure
子句将抛出异常:
def read (id: Int): Future[Option[Customer]] = {
val db = // ....
val customers = TableQuery[CustomerDB]
val action = customers.filter(_.id === id).result
val future = db.run(action.asTry)
future.map{
case Success(s) =>
if (s.length>0)
Some(s(0))
else
None
case Failure(f) => throw new Exception (f.getMessage)
}
}
现在,我的理解是,在 Scala 中应该使用 Try,而不是使用 try/catch/finally 异常。此外,不应抛出任何异常。但是如果没有抛出异常,如何通知上层出现了问题呢?
报告错误的正确方法是使用 Either。
trait Error
case class NotFound(id: Int) extends Error
case class QueryFailed(msg: String) extends Error
def read (id: Int): Future[Either[Error, Customer]] = {
val db = // ....
val customers = TableQuery[CustomerDB]
val action = customers.filter(_.id === id).result
val future = db.run(action.asTry)
future.map{
case Success(s) =>
if (s.length>0)
Right(s(0))
else
Left(NotFound(id))
case Failure(f) => Left(QueryFailed(f.getMessage))
}
}
Future
本身确实已经有 Try 里面了。所以,我会说你只需要展平(而且你的代码有点复杂,我简化了):
future.flatMap {
case Success(s) => Future.successful(s.headOption)
case Failure(f) => Future.failed(f)
}
结果 Future
当处于 failed
状态时通知调用者执行失败(包含原始异常)。否则,成功。
好的,一般来说,您可以使用 Future.successful 或 Future.failed(msg: String) 到 "signal" 上层(也称为调用方法),您是否获得了值。
更好的方法
然而,一个好的方法是在 Future 上使用 .recoverWith{}
以防失败。
例如:
def getUserFromCloud (userId: String): Future[String] = Future{
cloudProviderApi.getUsername(userId)
}.recoverWith{
Future.failed(s"$userId does not exist.")
}
调用方式呢?
好吧,你只需将成功映射到下划线并使用 recover 处理错误:
getUserFromCloud("test").map(_ => {
//In case of success
}).recover{
//In case of failure, like return BadRequest.
}
有关 recover
和 recoverWith
的更多信息,如果您有兴趣: