Scala Future 完成后进行清理的最佳实践
Best practice to make cleanup after Scala Future is complete
我想在 Future
完成后进行一些清理(例如关闭数据库连接)。
目前我是这样实现的:
Future { ... } onComplete {
case Success(v) =>
// ...
conn.close()
case Failure(ex) =>
// ...
conn.close()
}
有重复的代码,也很乏味。
对此有什么最佳实践吗?
我喜欢用map
,你可以这样做:
val mapped: Future[String] = future.map(_ => "OK").recover{case _ => "KO"}
由于对成功和失败都执行相同的操作 conn.close()
,因此考虑使用 andThen
将其作为副作用执行,例如
Future { ... } andThen { _ => conn.close() }
同样,使用 onComplete
我们可以做到
Future { ... } onComplete { _ => conn.close() }
andThen
和onComplete
的区别在于后者会returnUnit
,也就是丢弃returned的值Future
.
可悲的是,Scala Futures 没有内置这个基本功能。因此,我强烈建议使用像 ZIO 或 cats-effect 这样的现代效果系统,它们都解决了这个问题和无数其他问题期货有。做你想做的最简单的方法是使用 bracket
方法:
https://zio.dev/docs/overview/overview_handling_resources
现在 bracket
效果很好,但通常有一种方法效果更好:Managed
类型。如果您在获取资源时始终使用 Managed
,则几乎不可能编写泄漏资源的代码:
https://zio.dev/docs/datatypes/datatypes_managed
就是说,如果您绝对必须使用 Futures,则必须编写自己的 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))
}