Scala解析多个Future并获取Map(String,AnyRef)

时间:2018-09-06 21:48:17

标签: scala

我目前正在尝试一次解决多个期货,但是由于其中一些可能会失败,因此如果其中一个失败,我不想全部失败,而是以Map(String, AnyRef)结尾(表示具有将来名称的Map,并将响应转换为所需的地图。

目前,我有以下内容:

val fsResp = channelList.map {
  channelRef => channelRef.ask(ReportStatus).mapTo[EventMessage]
}

Future.sequence(fsResp).onComplete{
  case Success(resp: Seq[EventMessage]) =>
    resp.foreach { event => Supervisor.foreach(_ ! event) }

    val channels = loadConfiguredComponents()
      .collect {
        case ("processor" | "external", components) => components.map {
          case (name, config: Channel) =>
            (name, serializeDetails(config, resp.find(_.channel == ChannelName(name))))
        }
      }.flatten.toMap

    val event = EventMessage(...)

    Supervisor.foreach(_ ! event)

  case Failure(exception) => originalSender ! replayError(exception.getMessage)

}

但是,如果其中任何一个失败,则此操作将失败。那我怎么能得到Map(channelRef.path.name, event() | exception)呢?

谢谢!

2 个答案:

答案 0 :(得分:1)

您可以使用fallbackTo以避免失败。在此示例中,我将Future [T]更改为Future [Option [T]],以回退到None,然后删除None元素。

import scala.concurrent.ExecutionContext.Implicits.global

def method(value:Int) = { Thread.sleep(2000); println(value); value  }

println("start")

val eventualNone = Future.successful(None)

val futures = List(Future(method(1)), Future(method(2)), Future(method(3)), Future(throw new RuntimeException))

val withoutFailures = futures.map(_.map(Option.apply).fallbackTo(eventualNone))

Future.sequence(withoutFailures).map(_.flatten).onComplete {
  case Success(values) => println(values)
  case Failure(ex:Throwable) => println("FAIL")
}

Thread.sleep(5000)

输出

start
1
3
2
List(1, 2, 3)

如果您想知道失败的原因,可以将其更改为Either [Throwable,T]而不是Option [T]。

此代码始终为Success(关于Future结果),因此您需要检查您的值以了解所有期货是否失败。

答案 1 :(得分:0)

要从期货列表中捕获成功/失败值,您可以首先将map/recover应用于每个期货,然后使用Future.sequence将结果列表转换为List[Either[Throwable,EventMessage]]的期货,如以下简单示例所示:

import scala.concurrent.{Future, Await}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

case class EventMessage(id: Int, msg: String)

val fsResp = List(
  Future{EventMessage(1, "M1")}, Future{throw new Throwable()}, Future{EventMessage(3, "M3")}
)

val f = Future.sequence(
  fsResp.map( _.map{ resp =>
    // Do stuff with `resp`, add item to `Map()`, etc ...
    Right(resp)
  }.
  recover{ case e: Throwable =>
    // Log `exception` info, etc ...
    Left(e)
  } )
)

Await.result(f, Duration.Inf)
// f: scala.concurrent.Future[List[Product with Serializable with scala.util.
//   Either[Throwable,EventMessage]]] = Future(Success(List(
//     Right(EventMessage(1,M1)), Left(java.lang.Throwable), Right(EventMessage(3,M3))
//   )))