Return Scala 中用于 yield 的特定类型

Return a specific type from for yield in Scala

我正在尝试使用一个函数异步调用 3 个单独的 API,并将响应整理到一个 UserDetails 对象中,并将 return this 作为函数的一部分。

但是,我遇到的问题是,当我将 return 类型显式设置为 Future[UserDetails] 时,它不会编译,因为 yield 中的 return 是不是 UserDetails 对象,我不知道如何 return 从中得到我想要的东西。

注意我正在寻找 return 一个 Future[UserDetails] 但推断的 return 类型是 Future[Object]

我目前的代码如下:

def getUserDetails(userId: String) = {
  usersConnector.getUserById(userId).map {
    case Some(user) =>
      for {
        connections <- connectionsConnector.getAllConnections(user.username) recover {case _ => None}
        pendingConnections <- connectionsConnector.getAllPendingConnections(user.username) recover {case _ => None}
        userLocation <- userLocationsConnector.getLocationByUsername(user.username) recover {case _ => None}
      } yield {
        UserDetails(Some(user), connections, pendingConnections, userLocation)
      }
    case None => UserDetails(None, None, None, None)
  }
}

问题是您的地图函数中的案例 return 不是同一类型。 None 案例 returns UserDetails。但是,Some 案例 return 是 Future[UserDetails]。这两种类型的最小上界(least common ancestor)为AnyRef,即Object。结果,map 函数将 Future[Option[User]] 转换为 Future[Object].

有两种方法可以解决这个问题。首先是两个在 None 情况下使用 Future.successful 构造函数:

...
case None => Future.successful(UserDetails(None, None, None, None))
...

现在,生成的 future 的类型为 Future[Future[UserDetails]]。此时只需要把它压平:

...
}.flatMap(x => x) // add at the end

第二种更优雅的方法是将所有内容嵌入 for-comprehension:

def getUserDetails(userId: String) = {
  def detailsFor(user: Option[User]) = user match {
    case Some(user) =>
      for {
        connections <- connectionsConnector.getAllConnections(user.username) recover {case _ => None}
        pendingConnections <- connectionsConnector.getAllPendingConnections(user.username) recover {case _ => None}
        userLocation <- userLocationsConnector.getLocationByUsername(user.username) recover {case _ => None}
      } yield {
        UserDetails(Some(user), connections, pendingConnections, userLocation)
      }
    case None => Promise.successful(UserDetails(None, None, None, None))
  }
  for {
    user <- usersConnector.getUserById(userId)
    userDetails <- detailsFor(user)
  } yield userDetails
}

编辑:

请参阅下面 Łukasz 关于异步启动期货的评论。