flatmap, map
map就是对集合中的元素进行重映射,而flatmap则会将返回的值拆散然后重新组合。 下面举个直观的例子:
val buyingDonuts: Future[Boolean] = donutStock("plain donut").flatMap(qty => buyDonuts(qty))
val isSuccess = Await.result(buyingDonuts, 5 seconds) // true
flatMap返回的值是Future[Boolean]
val buyingDonuts: Future[Future[Boolean]] = donutStock("plain donut").map(qty => buyDonuts(qty))
val isSuccess = Await.result(buyingDonuts, 5 seconds) // Future(Success(true))
map返回的值是Future[Future[Boolean]]
import scala.concurrent.ExecutionContext.Implicits.global
val num1 = Future(1)
val num2 = Future(100)
val ans1 = num1.map(value1 => {
value1 + num2.value.get.get // 使用map这里要把第二个Future里解出来
})
ans1.foreach(println)
ans1.failed.foreach(println)
val ans2 = num1.flatMap(value1 => {
num2.map(value2 => value1 + value2) //这里不用解包
})
ans2.foreach(println)
ans2.failed.foreach(println)
Thread.sleep(3000)
// 输出:
// 101
// 101
在上面这个例子中,输出一致,好像没什么区别。无非是使用map要将num2里面的数据解出来。但是这里面有个问题就是:我们再解包num2里面的数据,不一定拿得到num2里面的数据,这时输出就是:
java.util.NoSuchElementException: None.get
101
那我们能不能用回调函数或者foreach呢?
回答是不能。因为使用回调函数和foreach的返回值都是空。
for
for 推导时间是就是上面的flatMap的简化形式。
注意,如果使用 for 表达式对 Future 做变换,一定要将 Future 声明在 for 循环的前面,否则 for 表达式将在串行的环境下完成它们。
有时候我们需要在获得一个Future之后再继续对其进行操作,有点类似于java中的管道,下面看一个例子:
println("Step 1: Define a method which returns a Future")
def donutStock(donut: String): Future[Int] = Future {
// assume some long running database operation
println("checking donut stock: " + donut)
10
}
println("\nStep 2: Define another method which returns a Future")
def buyDonuts(quantity: Int): Future[Boolean] = Future {
println(s"buying $quantity donuts")
true
}
上面我们又定义了一个方法,用来接收donutStock()的返回值,然后再返回一个Future[Boolean] 。
我们看下使用flatmap该怎么链接他们:
println("\nStep 3: Chaining Futures using flatMap")
val buyingDonuts: Future[Boolean] = donutStock("plain donut").flatMap(qty => buyDonuts(qty))
import scala.concurrent.Await
import scala.concurrent.duration._
val isSuccess = Await.result(buyingDonuts, 5 seconds)
println(s"Buying vanilla donut was successful = $isSuccess")
同样的,我们还可以使用for语句来进行链接:
println("\nStep 3: Chaining Futures using for comprehension")
for {
stock <- donutStock("vanilla donut")
isSuccess <- buyDonuts(stock)
} yield println(s"Buying vanilla donut was successful = $isSuccess")
Thread.sleep(3000)
如果我们只有两个维度或者一个维度,使用flatMap并无不妥,但是我们如果有多个维度的对象需要通过flatMap组合,那麻烦就比较大了。
import scala.concurrent.ExecutionContext.Implicits.global
val func1 = Future({
Thread.sleep(1000)
1
})
val func2 = Future({
Thread.sleep(1000)
2
})
val func3 = Future({
Thread.sleep(3000)
3
})
val res = for (
a <- func1;
b <- func2;
c <- func3
) yield (a, b, c) // 这里生成的是Future[(int,int,int)] 对象
res.foreach(println)
Thread.sleep(4000)
这样就很轻易的完成了多个Future的组合和同步