I'm creating three actor tasks using future, and then trying to collect all three when finished. The current code is the following:
implicit val timeout = Timeout(5.seconds)
val result1 = actor1 ? DataForActor(data)
val result2 = actor2 ? DataForActor(data)
val result3 = actor3 ? DataForActor(data)
val answer = for {
a <- result1.mapTo[List[ResultData]]
b <- result2.mapTo[List[ResultData]]
c <- result3.mapTo[List[ResultData]]
} yield (a ++ b ++ c).sorted
answer onComplete {
case Success(resultData) =>
log.debug("All actors completed succesffully")
successActor ! SuccessData(resultData.take(2))
case Failure(resultData) =>
log.info("actors failed")
}
Each of the actors (actor1, actor2, actor3) manipulates the data and returns either None or Option(List(resultData)), as shown in the following code:
val resultData = if(data.size == 0) None else {
data.map {
...
try {
... //manipulation on resultData
Option(resultData)
}
catch {
case e: Exception => None
}
}.flatten
}
The for statement concatenates lists from each actor, and produces a long List(resultData).
I want that in the case that one actor returns None, it's result in the for statement will not add anything to the concatenation, i.e. List().
An example:
If I get: result1 = List(1, 2, 3), result2 = None, result3 = List(4, 5),
I want: resultData = List(1, 2, 3, 4, 5)
You could replace None
with Nil
before mapTo
this way:
result1.map{
case None => Nil
case x => x
}.mapTo[List[ResultData]]
Note that you should avoid mapTo
with generic type like List
:
Future("x" :: Nil).mapTo[List[Int]]
// res0: scala.concurrent.Future[List[Int]]
Future("x" :: Nil).mapTo[List[Int]] foreach { _.map( _ + 1 ) }
// java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
Because of type erasure mapTo
can't prove that you have list of Int
, not List
of some other type. You'll get the same problem with case l: List[Int]
in receive
method of actor.
You should create special class for your messages like this:
sealed trait ResultList { def data: List[ResultData] }
case class NotEmptyResult(data: List[ResultData]) extends ResultList
case object EmptyResult extends ResultList { def data: List[ResultData] = Nil }
result1.mapTo[ResultList]