依赖期货

Dependent futures

开始玩 Scala futures,我被依赖的 futures 困住了。

让我们举个例子。我搜索地点并获得 Future[Seq[Place]]。对于这些地方中的每一个,我搜索最近的地铁站(服务返回 Future[List[Station]])。

我会这样写:

Place.get()
.map { places =>
    places.map { place =>
        Station.closestFrom(place).map { stations =>
            SearchResult(place, stations)
        }
    }
}

那东西会让我得到一个 Future[Seq[Future[SearchResult]]]... 这不是我所期望的。

我错过了什么才能得到 Future[Seq[SearchResult]]

谢谢大家,

阿尔班

您的解决方案中缺少两个 Future 概念:flatMapFuture.sequence

对每个进行解释:

flatMapmap 类似,只不过不是给它一个来自 future.map(A => B) 的函数,而是给它一个来自 future.flatMap(A => Future[B]) 的函数。这样您就可以将 Futures 链接在一起。

Future.sequence 是一个辅助函数,它将未来列表与列表的未来结合起来:Seq[Future[A]] => Future[Seq[A]]

使用 Future API 的这两个功能,我们可以将您的答案更改为:

Place.get().flatMap { places =>
    Future.sequence(places.map { place =>
        Station.closestFrom(place).map { stations =>
            SearchResult(place, stations)
        }
    })
}

简短版

通常使用 for-comprehension 比直接使用 futures 更容易 map/flatMap。在您的情况下,它应该如下所示:

for {places        <- Place.get()
     searchResults <- Future.traverse(places)(place => for (stations <- Station.closestFrom(place))
                                                       yield SearchResult(place,stations)
                                              )
} yield searchResults

详细版本

Future 作为 monad,它为您提供了多种链接操作的方法。

  • 如果您想将 'regular' 函数 f : A => B 应用于方框 myfuture : Future[A] 中的内容,确实 map 是获得 Future[B] 的方法.但是在目前的情况下 Station.closestFrom a 不会给你 List[Stattion] 而是 Future[List[Station]].
  • 如果你想应用一个单子操作 h : A => Future[B] 或链接其中的几个(这里 Places.getStation.closestFrom),flatMap 是可行的方法。将 h 应用于 Future[A] 得到 Future[B].
  • 如果要将单子操作 h : A => Future[B] 应用于 places : Seq[A] 之类的集合,则应使用 Future.traverse : Seq[A] => (A => Future[B]) => Future[Seq[B]].

此外,Scala 的 for-compresentation 只是 flatMap/map 的语法糖,因此您可以使用干净清晰的 for 循环,而不是直接使用它们编写复杂的代码。循环:

for { variable1 <- f1
      variable2 <- f2
} yield expression

等同于(没有优化):

f1.flatMap( variable1 => f2.map(variable2 => expression))

不要犹豫,使用 for-comprehension,它真的很有帮助。