2014-12-02 5 views
1

Я создаю три актерских задания, используя будущее, а затем пытаюсь собрать все три, когда закончим. текущий код выглядит следующим образом:Scala Future with Option()

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") 
    } 

Каждый из актеров (actor1, actor2, actor3) манипулирует данные и возвращает либо None или Option (List (resultData)), как показано в следующем коде:

val resultData = if(data.size == 0) None else { 
    data.map { 
    ... 
    try { 
     ... //manipulation on resultData 
     Option(resultData) 
    } 
    catch { 
     case e: Exception => None 
    } 
    }.flatten 
} 

Оператор for объединяет списки от каждого актора и производит длинный список (resultData).

Я хочу, чтобы в случае, когда один актер возвращает None, результат в инструкции for ничего не добавит к конкатенации, то есть List().

Пример:

Если я получаю: Result1 = Список (1, 2, 3), result2 = Нет, result3 = List (4, 5),

Я хочу: resultData = List (1, 2, 3, 4, 5)

+2

Пытаться вернуть тот же тип в любом случае. Вы должны либо вернуть 'Nil' вместо' None', либо использовать 'Option [List [ResultData]]'. – senia

ответ

3

Вы могли бы заменить None с Nil перед тем mapTo таким образом:

result1.map{ 
    case None => Nil 
    case x => x 
}.mapTo[List[ResultData]] 

Обратите внимание, что вы должны избегать mapTo с родовым типом, как 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 

Из-за типа стирания mapTo не может доказать, что у вас есть список из Int, не List каких-либо другого типа. У вас будет такая же проблема с case l: List[Int] в receive методом актера.

Вы должны создать специальный класс для ваших сообщений, как это:

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] 
+0

Спасибо за отличный ответ. Что касается вашего комментария относительно mapTo со списком обобщений, что бы вы порекомендовали вместо mapTo? – user3370773

+0

@ user3370773: вы можете использовать 'mapTo' с не общим классом (см. Обновление). Проблема не в методе «mapTo» - вы получите ту же проблему в методе 'receive' в акторе. – senia

Смежные вопросы