我想在Future
完成后进行一些清理(例如关闭数据库连接(。
目前我以这种方式实现它:
Future { ... } onComplete {
case Success(v) =>
// ...
conn.close()
case Failure(ex) =>
// ...
conn.close()
}
有重复的代码,也很乏味。
对此有什么最佳实践吗?
由于在成功和失败时都执行相同的操作conn.close()
,请考虑使用这样的andThen
将其作为副作用执行
Future { ... } andThen { _ => conn.close() }
同样,使用我们可以做的onComplete
Future { ... } onComplete { _ => conn.close() }
andThen
和onComplete
的区别在于后者会返回Unit
,即丢弃Future
的返回值。
可悲的事实是,Scala Futures没有内置这个基本功能。因此,我强烈建议使用像ZIO或猫效应这样的现代效果系统,它们都解决了这个问题以及Futures拥有的无数其他问题。执行所需操作的最简单方法是使用bracket
方法:
https://zio.dev/docs/overview/overview_handling_resources
现在bracket
工作得很好,但有一种方法通常效果更好:Managed
类型。如果在获取资源时始终使用Managed
,则几乎不可能编写泄漏资源的代码:
https://zio.dev/docs/datatypes/datatypes_managed
也就是说,如果你绝对必须使用期货,你必须编写你自己的 try-finally 等价物。或者你可以使用我的:
def tryFinally[A](tryy: => Future[A])(finallyy: => Future[Any])(
implicit ec: ExecutionContext): Future[A] =
Future.fromTry(Try(tryy)).flatten.transformWith { t =>
finallyy.flatMap((_: Any) => Future.fromTry(t))
}
我喜欢用map
,你可以做这样的事情:
val mapped: Future[String] = future.map(_ => "OK").recover{case _ => "KO"}