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.globalval 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.get101
那我们能不能用回调函数或者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 operationprintln("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.Awaitimport 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.globalval 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的组合和同步
