Best practice to make cleanup after Scala Future is complete
Asked Answered
A

3

5

I want to make some cleanup (like close db connection) after a Future is complete.
Currently I achieve it this way:

Future { ... } onComplete {
  case Success(v) =>
    // ...
    conn.close()
  case Failure(ex) =>
    // ...
    conn.close()
}

There's duplicate code and it's also tedious.
Is there any best practice to this?

Alyssaalyssum answered 25/12, 2019 at 9:46 Comment(0)
M
7

Since the same action conn.close() is performed on both success and failure, consider executing it as a side-effect using andThen like so

Future { ... } andThen { _ => conn.close() }

Similarly, using onComplete we could do

Future { ... } onComplete { _ => conn.close() }

The difference between andThen and onComplete is that latter will return Unit, that is, discard the returned value of the Future.

Medicate answered 25/12, 2019 at 10:16 Comment(0)
P
1

The sad truth is that Scala Futures don't have this basic feature built in. I would therefore strongly suggest using a modern effect system like ZIO or cats-effect, both of which solve this problem and a myriad of others that Futures have. The easiest way to do what you want is to use the bracket method:

https://zio.dev/docs/overview/overview_handling_resources

Now bracket works great, but there's a way that usually works even better: the Managed type. It's virtually impossible to write code that leaks resources if you consistently use Managed when acquiring resources:

https://zio.dev/docs/datatypes/datatypes_managed

That said,if you absolutely must use Futures, you'll have to write your own try-finally equivalent. Or you can use mine:

  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))
    }
Prescript answered 27/12, 2019 at 15:15 Comment(0)
S
0

I like to use map, you can do something like this:

val mapped: Future[String] = future.map(_ => "OK").recover{case _ => "KO"}
Supersonics answered 25/12, 2019 at 9:55 Comment(1)
Using two calls when one would do is not good practice. Use a single call like onComplete, andThen or transformChoplogic

© 2022 - 2024 — McMap. All rights reserved.