从List[Future[T]]到Future[List[T]]
在课程<Principles Of Reactive Programming>里Week3的一节 "Promises, promises, promises"中,Erik Meijer举了一个例子,实现一个函数:
def sequence[T](fs: List[Future[T]]): Future[List[T]] = {.....}
这个函数实际在Scala library的Future对象中有标准的实现。
def sequence[A, M[X] <: TraversableOnce[X]](in: M[Future[A]])(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], executor: ExecutionContext): Future[M[A]]
Simple version of Future.traverse. Transforms a TraversableOnce[Future[A]] into a Future[TraversableOnce[A]]. Useful for reducing many Futures into a single Future.
俺就想试着自己实现一下,于是写出了下面这段有问题的代码……
object ConcurrentTool {
def collect[T](futures: List[Future[T]]): Future[List[T]] = {
val p = Promise[List[T]]
var list = List.empty[T]
futures.foldLeft(list) {
(curr, future) => {
if (p.isCompleted)
curr
else {
future.onComplete {
case Failure(e) => p.failure(e)
case Success(e) => list = e :: list
}
list
}
}
}
if (!p.isCompleted)
p.success(list)
p.future
} def main(args: Array[String]) = {
def futureOne = {
Future {
1
}
}
def futureTwo = {
Future {
2
}
} val collection = collect(List(futureOne, futureTwo))
val lists = Await.result(collection, 1 seconds)
println(lists.size) }
}
collect方法用于将一个List[Future[T]]变成一个Future[List[T]]。然后我传给它两个future,等返回的Future[List[T]] compelete,然后取结果List的大小,打印出来。
但是结果是0……但是,在我加了断点进行调试时,有时结果是2,为啥呢? 错误不可怕,这是纠正自己的机会。
再来看一下collect方法的实现吧。我用一个Promise生成最后的future, 用一个空List做为foldLeft的初始值,然后遍历List[Future]里的所有future。对于每个future,给它注册一个回调函数,当它fail的时候,用引起fail的异常去complete跟最后结果相关的那个Promise,如果这个future成功了,就把它的结果附加在list里。在注册完回调之后,我返回保存结果的list。
问题就在这些操作的执行时间上。给future注册回调函数的动作是在main线程中,这个注册不会阻塞main线程的执行,假如被注册的函数的确是在另一个线程中执行的,那么在注册完回调函数之后,我返回的list仍然可能是最初的那个empty list, 所以在foldLeft完成后,foldLeft返回的仍然可能是那个empty list。接下来,我判断p.isCompleted, 如果否,我就用这个list去complete这个Promise,实际上我用一个empty list去complete了它,所以在获取collection的结果后,发现这个是一个空列表。
那么我们想要的结果如何实现呢?关键是,必须得生成这个Future[List]的所有future都compelete时,这个Future[List]才能complete。如何实现这一点呢?
- 我们可以阻塞执行collect方法的线程,显式地等待List[Future]里的所有future完成,再complete跟结果相关的那个Promise。但是这样做,会阻塞调用collect的线程,也违背了我们返回一个Future的目的。
- 我们可以在另一个线程里等待List[Future]里的所有future完成,再complete跟结果相关的那个Promise。在collect方法中,返回Promise对应的future。这样就不会阻塞调用collect的线程。
如何在另一个线程等待呢?可以用Await来阻塞等待,或者注册callback,使得当所有future完成时,callback被调用。
假如有两个Future,可以用下边的代码注册callback。
def waitBoth[T](futureA: Future[T], futureB: Future[T]): Future[List[T]] = {
val p = Promise[List[T]]()
futureA.onComplete{
case Failure(e) => p.failure(e)
case Success(t) => futureB.onComplete{
case Failure(eb) => p.failure(eb)
case Success(b) => p.success(t :: b :: Nil)
}
}
p.future
}
这个变形一下,用flatMap和map表示就是
def waitBoth[T](futureA: Future[T], futureB: Future[T]): Future[List[T]] = {
val p = Promise[List[T]]()
futureA.onComplete{
case Failure(e) => p.failure(e)
case Success(t) => futureA.flatMap{a: T =>
futureB map {b =>
p.success(a :: b :: Nil)
}
}
}
p.future
}
实际上Future的map和flatMap在实现时也用了Promise, 上边的代码简化一下就是
def waitBoth[T](futureA: Future[T], futureB: Future[T]): Future[List[T]] = {
futureA.flatMap { a: T =>
futureB map { b =>
a :: b :: Nil
}
}
}
再把flatMap和map转成for循环表示,就是
def waitTwo[T](futureA: Future[T], futureB: Future[T]): Future[List[T]] = {
for {
a <- futureA
b <- futureB
} yield a :: b :: Nil
}
那么如何组合更多的Future呢?我们来写一个方法把一个Future[T]的结果附加到Future[List[T]]中
def waitMore[T](futureA: Future[T], futures: Future[List[T]]): Future[List[T]] = {
for{
a <- futureA
b <- futures
}yield a :: b
}
然后以此为基础,就可以构造最早提到的sequence函数
def waitSome[T](futures: List[Future[T]]): Future[List[T]] = {
val p = Promise[List[T]]()
p.success(Nil)
val init: Future[List[T]] = p.future
futures.foldLeft(init){
(curr, f) => waitMore(f, curr)
}
}
上边代码关键在于了解到我们需要一个Future作为foldLeft的初始值,它必须是success的,且使其success的值为Nil。这个init实际上也可以用 val init: Future[List[T]] = Future{Nil}来得到。
这种形式距离<Principles Of Reactive Programming>给出的答案已经很接近了。实际上Erik Meijer给出了两个解法,其中跟这个相近的是
def sequence[T](fs: List[Future[T]]): Future[List[T]] = {
val successful = Promise[List[T]]
successful.success(Nil)
fs.foldRight(successful.future){
(f, acc) => for{x <-f; xs <- acc} yield x :: xs
}
}
从List[Future[T]]到Future[List[T]]的更多相关文章
- Transform java future into completable future 【将 future 转成 completable future】
Future is introduced in JDK 1.5 by Doug Lea to represent "the result of an asynchronous computa ...
- Future 异步回调 大起底之 Java Future 与 Guava Future
目录 写在前面 1. Future模式异步回调大起底 1.1. 从泡茶的案例说起 1.2. 何为异步回调 1.2.1. 同步.异步.阻塞.非阻塞 1.2.2. 阻塞模式的泡茶案例图解 1.2.3. 回 ...
- <2013 07 06> Future and Near Future
试图了解 量子力学 近现代基础物理学理论 量子计算机 脑科学 近现代生物学 遗传变异与进化 复杂工程学 系统工程 管理科学 人工智能 智能算法 机器学习 深度学习 大数据 云计算 ...
- 线程笔记:Future模式
线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascri ...
- java多线程系类:JUC线程池:06之Callable和Future(转)
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java并发编程:Callable、Future和FutureTask
作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...
- Scalaz(44)- concurrency :scalaz Future,尚不完整的多线程类型
scala已经配备了自身的Future类.我们先举个例子来了解scala Future的具体操作: import scala.concurrent._ import ExecutionContext. ...
- Java多线程与并发库高级应用-Callable与Future的应用
Callable这种任务可以返回结果,返回的结果可以由Future去拿 >Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的. >Completion ...
- Callable 和 Future接口 学习
* Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务. * Callable和Runnable有几点不同: * (1)C ...
随机推荐
- 和阿文一起学H5-文字云制作
---恢复内容开始--- 实用工具!优秀的标签云免费生成工具 来源:http://www.uisdc.com/online-word-cloud-generators 标签云或文字云是关键词的视觉化描 ...
- iOS中的动画
iOS中的动画 Core Animation Core Animation是一组非常强大的动画处理API,使用它能做出非常绚丽的动画效果,而且往往是事半功倍,使用它需要添加QuartzCore .fr ...
- Xcode6模拟器时BUG导致键盘无法弹出
Xcode 6.X版本可能会出现虚拟键盘无法调出,并在控制台出现如下提示:Can't find keyplane that supports type 5 for keyboard iPhone-Po ...
- jquery-ui 中treegird 逐步加载
官方网站上没有ajax逐步加载的例子,自己研究了下 js代码 $("#bomStructureTable").treegrid({ url : "systemcontro ...
- maven安装配置(myeclipse)(一)
欢迎转载:http://www.cnblogs.com/shizhongtao/p/3374130.html 对于我来说,maven主要用于jar包的管理,避免项目中频繁更换jar的版本,以及网上搜索 ...
- UVALive 2889(回文数字)
题意:给定i,输出第i个回文数字. 分析:1,2,3,4,……,9------------------------------------------------------------------- ...
- Adobe Illustrator CS6 绿色简体中文版下载地址
一.Adobe Illustrator CS6 简体中文精简绿色优化版:1.由官方简体中文正式版制作而成,只需要执行一次快速安装即可使用.已经注册,非tryout版,支持x64位系统.2.精简了Ext ...
- jquery——ajax加载后的内容,单击事件失效
使用delegate(),on()绑定事件,可以将事件绑定到其祖先元素上,这样以后加载出来的元素,单击事件仍然有效
- ng-src作用
... <ul class="phones"> <li ng-repeat="phone in $ctrl.phones | filter:$ctrl. ...
- Android图像处理2
此次实验主要通过Android中的方法获取输入的颜色矩阵的值,更改后赋值给图片中的颜色矩阵更改图片效果.具体的布局的方法跟笔记1种差不多,只不过这里要添加一个供用户输入的GridView <Gr ...