flatmap, map

map就是对集合中的元素进行重映射,而flatmap则会将返回的值拆散然后重新组合。 下面举个直观的例子:

  1. val buyingDonuts: Future[Boolean] = donutStock("plain donut").flatMap(qty => buyDonuts(qty))
  2. val isSuccess = Await.result(buyingDonuts, 5 seconds) // true

flatMap返回的值是Future[Boolean]

  1. val buyingDonuts: Future[Future[Boolean]] = donutStock("plain donut").map(qty => buyDonuts(qty))
  2. val isSuccess = Await.result(buyingDonuts, 5 seconds) // Future(Success(true))

map返回的值是Future[Future[Boolean]]


  1. import scala.concurrent.ExecutionContext.Implicits.global
  2. val num1 = Future(1)
  3. val num2 = Future(100)
  4. val ans1 = num1.map(value1 => {
  5. value1 + num2.value.get.get // 使用map这里要把第二个Future里解出来
  6. })
  7. ans1.foreach(println)
  8. ans1.failed.foreach(println)
  9. val ans2 = num1.flatMap(value1 => {
  10. num2.map(value2 => value1 + value2) //这里不用解包
  11. })
  12. ans2.foreach(println)
  13. ans2.failed.foreach(println)
  14. Thread.sleep(3000)
  15. // 输出:
  16. // 101
  17. // 101

在上面这个例子中,输出一致,好像没什么区别。无非是使用map要将num2里面的数据解出来。但是这里面有个问题就是:我们再解包num2里面的数据,不一定拿得到num2里面的数据,这时输出就是:

  1. java.util.NoSuchElementException: None.get
  2. 101

那我们能不能用回调函数或者foreach呢?
回答是不能。因为使用回调函数和foreach的返回值都是空。

for

for 推导时间是就是上面的flatMap的简化形式。
注意,如果使用 for 表达式对 Future 做变换,一定要将 Future 声明在 for 循环的前面,否则 for 表达式将在串行的环境下完成它们。

有时候我们需要在获得一个Future之后再继续对其进行操作,有点类似于java中的管道,下面看一个例子:

  1. println("Step 1: Define a method which returns a Future")
  2. def donutStock(donut: String): Future[Int] = Future {
  3. // assume some long running database operation
  4. println("checking donut stock: " + donut)
  5. 10
  6. }
  7. println("\nStep 2: Define another method which returns a Future")
  8. def buyDonuts(quantity: Int): Future[Boolean] = Future {
  9. println(s"buying $quantity donuts")
  10. true
  11. }

上面我们又定义了一个方法,用来接收donutStock()的返回值,然后再返回一个Future[Boolean] 。
我们看下使用flatmap该怎么链接他们:

  1. println("\nStep 3: Chaining Futures using flatMap")
  2. val buyingDonuts: Future[Boolean] = donutStock("plain donut").flatMap(qty => buyDonuts(qty))
  3. import scala.concurrent.Await
  4. import scala.concurrent.duration._
  5. val isSuccess = Await.result(buyingDonuts, 5 seconds)
  6. println(s"Buying vanilla donut was successful = $isSuccess")

同样的,我们还可以使用for语句来进行链接:

  1. println("\nStep 3: Chaining Futures using for comprehension")
  2. for {
  3. stock <- donutStock("vanilla donut")
  4. isSuccess <- buyDonuts(stock)
  5. } yield println(s"Buying vanilla donut was successful = $isSuccess")
  6. Thread.sleep(3000)

如果我们只有两个维度或者一个维度,使用flatMap并无不妥,但是我们如果有多个维度的对象需要通过flatMap组合,那麻烦就比较大了。

  1. import scala.concurrent.ExecutionContext.Implicits.global
  2. val func1 = Future({
  3. Thread.sleep(1000)
  4. 1
  5. })
  6. val func2 = Future({
  7. Thread.sleep(1000)
  8. 2
  9. })
  10. val func3 = Future({
  11. Thread.sleep(3000)
  12. 3
  13. })
  14. val res = for (
  15. a <- func1;
  16. b <- func2;
  17. c <- func3
  18. ) yield (a, b, c) // 这里生成的是Future[(int,int,int)] 对象
  19. res.foreach(println)
  20. Thread.sleep(4000)

这样就很轻易的完成了多个Future的组合和同步