使用返回期权未来的函数映射后扁平化期权的未来



我有一个类型Future[Option[String]]的集合,我将其映射到返回Future[Option[Profile]]的函数,但这创建了一个返回类型Future[Option[Future[Option[Profile]]]] queryProfile因为返回类型是"未来[选项[配置文件]]"

val users: Future[Option[User]] = someQuery
val email: Future[Option[String]] = users map(opSL => opSL map(_.email) )
val userProfile = email map {opE => opE map {E => queryProfile(E)}}

我需要在不解压缩所有这些关卡的情况下使用包含在val userProfile深处的Profile对象,使用flatMap或"扁平化"的正确方法是什么,或者有更好的方法

你可以得到一个"部分未来",如下所示:

  val maybeProfile: Future[Profile] = users
    .collect { case Some(u) => u.email }
    .flatMap { email => queryProfile(email) }
    .collect { case Some(p) => p }

现在maybeProfile包含(完全"裸"/未包装)Profile实例,但前提是它能够找到它。您可以像往常一样.map它来使用它做其他事情,这将以通常的方式工作。

如果您想阻止并等待完成,则必须在某个时候处理丢失的情况。例如:

   val optionalProfile: Option[Profile] = Await.result(
     maybeProfile
       .map { p => Some(p) } // Or just skip the last `collect` above
       .recover { case _:NoSuchElementException => None },
     1 seconds
   )

如果你对只有Future[Option[Profile]]感到满意,并且更愿意有"解开"的魔法,并将丢失的案例本地化在一个地方,你可以像这样把上面的两个片段放在一起:

val maybeProfile: Future[Option[Profile]] = users 
  .collect { case Some(u) => u.email }
  .flatMap { email => queryProfile(email) }
  .recover { case _:NoSuchElementException => None }

或者像另一个答案建议的那样使用Option.fold

val maybeProfile: Future[Option[Profile]] = users
    .map { _.map(_.email) }
    .flatMap { _.fold[Future[Option[Profile]]](Future.successful(None))(queryProfile) }

就个人而言,我发现最后一个选项的可读性较差。

就我个人而言,我认为像 scalaz/cats 提供的 OptionT 这样的 monad 变压器将是最干净的方法:

val users = OptionT[Future,User](someQuery)
def queryProfile(email:String) : OptionT[Future,Profile] = ...
for {
  u <- users
  p <- queryProfile(u.email)
} yield p

我只是创建一个这样的帮助程序方法:

private def resolveProfile(optEmail: Option[String]): Future[Option[Profile] =
  optEmail.fold(Future.successful(None)) { email =>
    queryProfile(email).map(Some(_))
  }

然后,您可以像这样flatMap原始email未来:

val userProfile = email.flatMap(resolveProfile) 

最新更新