Reputation: 1071
In the code below I have two Play for Scala functions, the first one catches an exception (this works fine) and in the second one I'm trying to rewrite it using Try
.
I have two problems with Try
: (1) when the number is negative the method doesn't fail, (2) I need to wrap all the responses with Future.successful
.
How to fix this code?
class Test extends Controller {
def test1 = Action.async { request =>
val future = isPositive(-1)
future.map { result =>
Ok("OK, it's positive")
}
.recover {
case e => Ok(e.getMessage)
}
}
def isPositive(i: Int) = Future {
if (i<0)
throw new Exception ( "Number is negative" )
else
i
}
def test2 = Action.async { request =>
isPositiveWithTry(-1) match {
case Success(s) => Future.successful(Ok("OK, it's positive (Try succeded)"))
case Failure(f) => Future.successful(Ok(f.getMessage + " (Try failed)"))
}
}
def isPositiveWithTry(i: Int) : Try[Future[Int]] = Try {
isPositive(i)
}
}
Upvotes: 0
Views: 179
Reputation: 14825
In isPositive
method exceptions are already caught by Future
def isPositive(i: Int) = Future {
if (i<0)
throw new Exception ( "Number is negative" )
else
i
}
In the below code
def isPositiveWithTry(i: Int) : Try[Future[Int]] = Try {
isPositive(i)
}
isPositive
already catches all expections and Try
is always a success
.
So, when i
is negative. Exception raised are handled by future and try gets a success value, resultant Try is a success. So you get successful Try with a failed Future inside.
Assume throwing the exception as blowing up a Grenade.
Assume Future
and Try
as two layers. When grenade is blasted inside the double layer of Try[Future]
i.e Try is around Future and grenade is gone off in the Future.
Now Future
withstands the blast and becomes a failed value. As Future already took the damage caused by the damage of exception(grenade). Try will be a success but the value inside the Try is a failed future value. That failed future value is nothing but the exception raised.
You can refactor your code to below one
Get rid of isPositiveWithTry
. This method is not needed.
def isPositive(i: Int) = Future {
if (i<0)
throw new Exception ( "Number is negative" )
else
i
}
def test2 = Action.async { request =>
isPositive(-1).flatMap { _ =>
Future.successful(Ok("OK, it's positive (Try succeded)"))
}.recoverWith {
case f: Throwable => Future.successful(Ok(f.getMessage + " (Try failed)"))
}
}
Again test2
can also be written as
def test2 = Action.async { request =>
isPositive(-1).map { _ =>
Ok("OK, it's positive (Try succeded)")
}.recover {
case f: Throwable => Ok(f.getMessage + " (Try failed)")
}
}
In case isPositive
returns Try
def isPositive(i: Int) = Try {
if (i<0)
throw new Exception ( "Number is negative" )
else
i
}
Now test2
will look like
def test2 = Action.async { request =>
isPositive(-1) match {
case Success(s) => Future.successful(Ok("OK, it's positive (Try succeded)"))
case Failure(f) => Future.successful(Ok(f.getMessage + " (Try failed)"))
}
}
Upvotes: 1
Reputation: 35970
Couple points:
1) You need to rewrite your isPositive
such that it does not surround itself via a Future
. The Future
is catching the exception.
def isPositive(i: Int) ={
if (i<0)
throw new Exception ( "Number is negative" )
else
i
}
2) If you have a Try
and you want a Future
, then you can use the method on the companion object of Future
, Future.fromTry
. That will take a Try
and turn it into the correct state of a Future
.
Upvotes: 1