We are developing some systems in our company in Scala and we have some doubts. We were discussing about how to map the future exceptions and we don't know when we should use the option 1 or the option 2.
val created: Future[...] = ???
Option 1:
val a = created recover { case e: database.ADBException => logger.error("Failed ...", e) throw new business.ABusinessException("Failed ...", e) }
Option 2:
val a = created recoverWith { case e: database.ADBException => logger.error("Failed ...", e) Future.failed(new business.ABusinessException("Failed ...", e)) }
Could someone explain when should i do the option 1 or the option 2? What is the diff?
Recover requires a partial function which matches an exception and returns the default value. Whereas, recoverWith is used to match an exception and substitute it with another Failure . Using recover to provide a default value.
NOTE: With Future. onComplete() we are no longer blocking for the result from the Future but instead we will receive a callback for either a Success or a Failure.
Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.
Well, the answer is clearly described in scaladocs:
/** Creates a new future that will handle any matching throwable that this * future might contain. If there is no match, or if this future contains * a valid result then the new future will contain the same. * * Example: * * {{{ * Future (6 / 0) recover { case e: ArithmeticException => 0 } // result: 0 * Future (6 / 0) recover { case e: NotFoundException => 0 } // result: exception * Future (6 / 2) recover { case e: ArithmeticException => 0 } // result: 3 * }}} */ def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = { /** Creates a new future that will handle any matching throwable that this * future might contain by assigning it a value of another future. * * If there is no match, or if this future contains * a valid result then the new future will contain the same result. * * Example: * * {{{ * val f = Future { Int.MaxValue } * Future (6 / 0) recoverWith { case e: ArithmeticException => f } // result: Int.MaxValue * }}} */ def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = {
recover
wraps plain result in Future
for you (analogue of map
), while recoverWith
expects Future
as the result (analogue of flatMap
).
So, here is rule of thumb:
If you recover with something that already returns Future
, use recoverWith
, otherwise use recover
.
update In your case, using recover
is preferred, as it wraps the exception in Future
for you. Otherwise there is no performance gain or anything, so you just avoid some boilerplate.
Using recoverWith
you are asked to return a wrapped future, using recover
you are asked to throw an exception.
.recoverWith # => Future.failed(t) .recover # => throw t
I prefer using recoverWith
because I think functional programming prefers returning objects than throwing exceptions which is less of a functional style, even if it's internal code block, I think it still holds..
However if I have an internal piece of code in the recovery block which might throw an exception then, in that case, rather than catching it and wrapping it with Future
, or Trying it, I might as well just run this piece of code in combination with recover
and it would handle the exception wrapping for you, which would make the code more readable and compact.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With