在 scala 中处理用 Try 包裹的未来
Dealing with a Future wrapped with Try in scala
我有一个 returns 未来的回购方法:
def insert(newUser: User): Future[User]
此方法可能因不同原因而失败,但是,我希望能够捕获这些原因之一并重新抛出其他原因。
如果我只是用 Try
结束调用
Try(userRepository.insert(usr)) match {
case Success(newUserFuture) => newUserFuture
case Failure(e) if e.getCause.getMessage.contains("duplicate key") => {
logger.warn(e.getMessage)
throw DuplicateEntityException("duplicate user record")
}
case Failure(e) => {
logger.error(e.getMessage)
throw e
}
}
结果,我得到 Try[Future[User]]
,在这种情况下,两种失败都不会发生,因为 Future 已成功创建。
在这一点上,我不确定如何处理它。理想情况下,我想从中得到 Future[Either[String, User]]
(或类似的东西)。
我应该如何处理这种情况,还是我的方向完全错误?
根据建议编辑
可能的解决方案
我试过这样的事情:
userRepository.insert(usr).map(newUser => Right(newUser)) recoverWith {
case e: PSQLException if e.getMessage.contains("duplicate key") =>
Future.successful(Left("duplicate user record"))
}
我认为你可以颠倒你的方法,在这种情况下使用守卫来稍微清理一下代码。首先是让 userRepository.insert(usr)
return 一个 Future[Try[User]]
,这样你就可以映射未来,例如
def insert(user: User): Future[Try[User]]
然后可以这样使用:
userRepository.insert(user).map {
case Success(insertedUser) => Right(insertedUser)
case Failure(ex) if ex.getCause.getMessage.contains("duplicate key") => throw DuplicateEntityException("duplicate user record")
case Failure(ex) => throw ex
}
这将是一个 Future[User]
,您可以将回调附加到(请参阅 http://docs.scala-lang.org/overviews/core/futures.html)以对用户执行其他操作或处理异常。
Future
将为您处理异常。您不需要用 Try
.
包装未来的代码
Future
也完成 Try
的工作。
不需要这行代码
Try(userRepository.insert(usr))
我希望你的插入方法是这样的
def insert(newUser: User): Future[User] = Future { doSomeDBCall() }
在上面的方法中。如果 doSomeDBCall()
失败。该异常将被 future 安全地捕获,并将作为 Failure(exception)
传递给调用者。所以你不需要用 Try
包裹 Future
捕获特定异常并抛出其他异常(传播其他异常)。
您可以使用 recoverWith
处理异常并传播您不感兴趣的异常。
在下面的代码中 SQLException
被处理,其他异常被传播给调用者。
userRepository.insert(usr).recoverWith {
case ex: SQLException => Future.successful(defaultValue)
case ex => Future.failed(ex)
}
尝试这样的事情:
def futureToFutureTry[T](f: Future[T]): Future[Try[T]] =
f.map(Success(_)).recover {
case e: Throwable => {
Failure(e)
}
}
...
userRepository.insert(usr) map futureToFutureTry
用于检查
def checkOperations[A](operations: List[Try[A]]): Future[List[A]] = {
Future {
if (!operations.filter(_.isFailure).isEmpty) {
// there were some error
} else {
// all operations are ok
}
}
我有一个 returns 未来的回购方法:
def insert(newUser: User): Future[User]
此方法可能因不同原因而失败,但是,我希望能够捕获这些原因之一并重新抛出其他原因。 如果我只是用 Try
结束调用Try(userRepository.insert(usr)) match {
case Success(newUserFuture) => newUserFuture
case Failure(e) if e.getCause.getMessage.contains("duplicate key") => {
logger.warn(e.getMessage)
throw DuplicateEntityException("duplicate user record")
}
case Failure(e) => {
logger.error(e.getMessage)
throw e
}
}
结果,我得到 Try[Future[User]]
,在这种情况下,两种失败都不会发生,因为 Future 已成功创建。
在这一点上,我不确定如何处理它。理想情况下,我想从中得到 Future[Either[String, User]]
(或类似的东西)。
我应该如何处理这种情况,还是我的方向完全错误?
根据建议编辑 可能的解决方案 我试过这样的事情:
userRepository.insert(usr).map(newUser => Right(newUser)) recoverWith {
case e: PSQLException if e.getMessage.contains("duplicate key") =>
Future.successful(Left("duplicate user record"))
}
我认为你可以颠倒你的方法,在这种情况下使用守卫来稍微清理一下代码。首先是让 userRepository.insert(usr)
return 一个 Future[Try[User]]
,这样你就可以映射未来,例如
def insert(user: User): Future[Try[User]]
然后可以这样使用:
userRepository.insert(user).map {
case Success(insertedUser) => Right(insertedUser)
case Failure(ex) if ex.getCause.getMessage.contains("duplicate key") => throw DuplicateEntityException("duplicate user record")
case Failure(ex) => throw ex
}
这将是一个 Future[User]
,您可以将回调附加到(请参阅 http://docs.scala-lang.org/overviews/core/futures.html)以对用户执行其他操作或处理异常。
Future
将为您处理异常。您不需要用 Try
.
Future
也完成 Try
的工作。
不需要这行代码
Try(userRepository.insert(usr))
我希望你的插入方法是这样的
def insert(newUser: User): Future[User] = Future { doSomeDBCall() }
在上面的方法中。如果 doSomeDBCall()
失败。该异常将被 future 安全地捕获,并将作为 Failure(exception)
传递给调用者。所以你不需要用 Try
Future
捕获特定异常并抛出其他异常(传播其他异常)。
您可以使用 recoverWith
处理异常并传播您不感兴趣的异常。
在下面的代码中 SQLException
被处理,其他异常被传播给调用者。
userRepository.insert(usr).recoverWith {
case ex: SQLException => Future.successful(defaultValue)
case ex => Future.failed(ex)
}
尝试这样的事情:
def futureToFutureTry[T](f: Future[T]): Future[Try[T]] =
f.map(Success(_)).recover {
case e: Throwable => {
Failure(e)
}
}
...
userRepository.insert(usr) map futureToFutureTry
用于检查
def checkOperations[A](operations: List[Try[A]]): Future[List[A]] = {
Future {
if (!operations.filter(_.isFailure).isEmpty) {
// there were some error
} else {
// all operations are ok
}
}