Scala 2.10中的新Future为每个异步调用操作的操作使用执行上下文(包括map
,filter
等)。这是否意味着每个操作将始终通过执行上下文单独调用,或者在使用相同的执行上下文链接多个转换/过滤器时是否可以优化此步骤?
即。如果执行f.map(...).filter(...).map(...)
,所有具有相同的执行上下文,这将调用execute()
一次(因为它足够巧妙地从上面组成同步函数),或三次?
如果scala的未来没有进行上述优化,是否有一个替代框架更适合做上述的长链组合?
答案 0 :(得分:10)
我无法提供任何文档链接,这些链接将清楚地说明将要发生的事情,但我们可以进行一个简单的实验来回答您的问题。
只需打开Scala REPL并粘贴以下代码:
import java.util.concurrent.Executors
import scala.concurrent._
implicit val ec = new ExecutionContext {
val threadPool = Executors.newFixedThreadPool(1000);
def execute(runnable: Runnable) {
threadPool.submit(runnable)
println("execute!")
}
def reportFailure(t: Throwable) {}
}
future { 1 } map(_ + 1) filter (_ > 0) map (_ + 2)
它将打印:
阶> future {1} map(_ + 1)filter(_> 0)map(_ + 2)
执行!
执行!
执行!
执行!
res0:scala.concurrent.Future [Int] = scala.concurrent.impl.Promise$DefaultPromise@7ef3de76
因此,对于您正在执行的每个操作都会调用execute(并且您可以在文档中检查每个函数,如map或filter,将ExecutionContext作为隐式参数:http://www.scala-lang.org/api/2.10.6/#scala.concurrent.Future)
如果您正在寻找替代框架,您应该检查scalaz Futures。我没有他们的经验,但他们似乎是你在寻找。检查此主题:https://groups.google.com/forum/#!msg/scalaz/-PuakIf-g_4/7ydrU5VIfDQJ
与scala 2.10中的
Future
实现不同,map
和flatMap
不会生成新任务,也不需要隐式ExecutionContext
。相反,map
和flatMap
仅添加到将由“当前”线程运行的当前(trampolined)延续,除非通过Future.fork
或Future.apply
明确分叉。这意味着Future
实现了比2.10实现更好的线程重用,并避免了不必要的线程池提交周期。
Future
也与scala 2.10Future
类型不同,因为它不一定代表正在运行的计算。相反,我们使用scalaz.Nondeterminsm
接口的函数重新引入非确定性 。这简化了我们的实现并使代码更易于推理,因为效果的顺序和非确定性的要点是完全明确的,并且不依赖于Scala的评估顺序。重要说明:
Future
不包含任何错误处理,通常只能用作想要构建Future
功能但希望设计自己错误的库编写者的构建块处理策略。有关通过正确错误处理扩展scalaz.concurrent.Task
的类型,请参阅Future
- 它只是Future[Either[Throwable,A]]
的包装器,具有许多其他便利功能。