Why does it not catch and print the exception msg from calculateTwo? If we make calculateOne throw the exception, the exception is caught and the msg is printed.
package com.oxo.test
import cats.data.EitherT
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object FutureError extends App {
try {
(for {
result <- EitherT(calculateOne())
resultTwo <- EitherT(calculateTwo())
resultThree <- EitherT(calculateThree())
sum = result + resultTwo + resultThree
} yield {
sum
})
} catch {
case e: Exception => println(e.getMessage)
}
private def calculateOne(): Future[Either[String, Int]] = {
//throw new RuntimeException("error from one")
Future.successful(Right(1))
}
private def calculateTwo(): Future[Either[String, Int]] = {
//throw new RuntimeException("error from two")
//Future.successful(Right(2))
}
private def calculateThree(): Future[Either[String, Int]] = {
//throw new RuntimeException("error from three")
Future.successful(Right(3))
}
}
========= UPDATE =================
From the responses I understand that any exception from first future get thrown right away and any errors from subsequent Futures results in Future.failed. Since we don't want to mix and match with try catch, Is there any elegant way for the below code to continue processing the loop? Irrespective of from where the exception is thrown, handle the error and move on to next element.
package com.oxo.test
import cats.data.EitherT
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object FutureError extends App {
val processingList = List(1, 2, 3)
processingList.map { x =>
val k = (for {
result <- EitherT(calculateOne(x))
resultTwo <- EitherT(calculateTwo(x))
resultThree <- EitherT(calculateThree(x))
sum = result + resultTwo + resultThree
} yield {
sum
}).value
k.map {
case Right(value) =>
println("This gets printed" + value);
case Left(value) =>
println("This error gets printed" + value);
}.recover {
case _ =>
println("processing value" + x);
println("This error gets printed");
}
}
private def calculateOne(input: Int): Future[Either[String, Int]] = {
throw new RuntimeException("error from one")
//Future.successful(Right(input + 1))
}
private def calculateTwo(input: Int): Future[Either[String, Int]] = {
//throw new RuntimeException("error from two")
Future.successful(Right(input + 2))
}
private def calculateThree(input: Int): Future[Either[String, Int]] = {
//throw new RuntimeException("error from three")
Future.successful(Right(input + 3))
}
}
EitherTandtry ... catchare two completely orthogonal exception handling mechanisms. To deal with result-type-style exceptions likeEitherT, we simply call methods on the class, rather than using a special control construct. For instance,getOrElseis a good choice in your situation.Keep in mind that handling the exception does not excuse you from returning a value of the appropriate type. If
sumis anInt, then the result of handling error conditions also needs to be anInt. If you're not returning anIntin that case, you're not handling the exception; you're propagating it.