张贵宾
2011.10.29
注:翻译这些英文书籍资料纯属个人爱好,如有不恰当之处敬请指正。
比如,假设我们要从一个指定的URL上为所有的图片渲染一个概览。我们可以在图片下载完毕后,再依次渲染每张单独的图片。为了增加程序的吞吐量,可以让每个loader用自己的actor去单独下载,既然每个执行下载的actor都执行的是注重结果的任务,那么就可以很方便的使用future来跟踪期望的结果。下面的代码就展示了用这种方式渲染图片。
def renderImages(url: String) { val imageInfos = scanForImageInfo(url) val dataFurures = for(info <- imageInfos) yield { val loader = actor { react { case Download(info) => reply(info.downloadImage()) } } loader !! Download(info) } for (i <- 0 until imageInfos.size) { dataFuture(i)() match { case data@ImageData(_) => renderImage(data) } } println("OK, all images rendered") }
我们刚才描述的实现方式在等待future的结果时将会阻塞底层的线程。然而,我们也可以使用react以非阻塞的、基于消息的方式等待future。这样做的关键是与每个future相关联的InputChannel,这个channel是用来把future的结果传输给创建future的actor。使用基于线程的receive,调用future的apply方法会等待收取这个channel上的结果。然而我们也能在future的Channel上够使用react用基于事件的方式等待结果。
def renderImages(url: String) { val imageInfos = scanForImageInfo(url) val dataFutures = for(info <- imageInfos) yield { val loader = actor { react { case Download(info) => reply(info.downloadImage()) } } loader !! Download(info) } var i = 0 loopWhile(i < imageInfos.size) { i += 1 dataFutures(i - 1).inputChannel.react{ case data@ImageData(_) => renderImage(data) } } andThen {println("OK, all images rendered.")} }
你也可以建立一个客户化的控制流组合器来在for循环内部使用react。在下面的章节中我们将解释如何做到这一点的。
List5.9
def renderImages(url: String) { val imageInfos = scanForImageInfo(url) val dataFutures = for(info <- imageInfos) yield { val loader = actor { react { case Download(info) => reply(info.downloadImage()) } } loader !! Download(info) } (for (ft <- ForEach(dataFutures)) ft.inputChannel.react { case data@ImageData(_) => renderImage(data) } andThen { println("OK, all images rendered.") } ) }
case class ForEach[T] (iter: Iterable[T]) { def foreach (fun: T => Unit): Unit = { val it = iter.elements // 在Scala2.9中elements方法已经废弃,使用iterator方法。 loopWhile(it.hasNext) { fun(t.next) } } }
代码5.9展示了使用客户化的ForEach操作符遍历一个list,在遍历的同时调用每个元素的react方法。在这种情况下,我们希望遍历dataFutures中的future。我们使用ForEach把dataFutures list转换成一个类似复合for循环中的生成器对象,它生成了与dataFutures list中相同的值,也就是说生成了所有的list中的元素。然而,ForEach做到了即使在调用了for循环内部的react之后,还能够继续遍历。
代码5.10展示了ForEach的具体实现。把ForEach设计成case class主要是为了在创建实例时能够省略new关键字。ForEach的构造函数接收一个类型为Iterable[T]的参数--这个集合参数用于生成我们自己的迭代器。
ForEach类有一个唯一的方法foreach,foreach方法接收一个函数类型的参数 T => Unit,实现foreach方法使得ForEach类的实例能够在简单的for复合循环中被用作生成器,就像代码5.9中展示的那样。在复合for循环中被绑定到生成元素的变量对应于函数类型的参数fun,复合循环的循环体对应于函数类型参数fun的函数体。
在foreach方法内部,我们首先从Iterable中获得了迭代器it,然后我们使用it和loopWhile组合器遍历集合,在每一次迭代过程中,我们对当前的集合元素应用函数方法fun,既然我们在使用loopWhile,那么在fun方法中调用react就很安全。