问题
The new Future in Scala 2.10 uses an execution context for every operation where an action is called asynchronously (including map
, filter
, etc). Does this mean that every action will always be called individually through the execution context, or is it possible that this step is optimized away when chaining multiple transformations/filters each using the same execution context?
I.e. if doing f.map(...).filter(...).map(...)
, all with the same execution context, will this call execute()
once (because it's clever enough to compose a synchronous function from the above), or three times?
If the scala future does not do the above optimization, is there an alternative framework better suited for long chained compositions that does do the above?
回答1:
I cannot provide any link to documentation which will clearly state what will really happen, but we can conduct a simple experiment which will answer your question.
Just open the Scala REPL and paste the following code:
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)
It will print:
scala> future { 1 } map(_ + 1) filter (_ > 0) map (_ + 2)
execute!
execute!
execute!
execute!
res0: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@7ef3de76
So execute is called for every single operation you are doing (and as you can check in the documentation each function like map or filter takes ExecutionContext as an implicit parameter: http://www.scala-lang.org/api/2.10.6/#scala.concurrent.Future)
If you are looking for an alternative framework you should check scalaz Futures. I have no experience with them, but they seems to be what you are looking for. Check this thread: https://groups.google.com/forum/#!msg/scalaz/-PuakIf-g_4/7ydrU5VIfDQJ
Unlike the
Future
implementation in scala 2.10,map
andflatMap
do NOT spawn new tasks and do not require an implicitExecutionContext
. Instead,map
andflatMap
merely add to the current (trampolined) continuation that will be run by the 'current' thread, unless explicitly forked viaFuture.fork
orFuture.apply
. This means thatFuture
achieves much better thread reuse than the 2.10 implementation and avoids needless thread pool submit cycles.
Future
also differs from the scala 2.10Future
type in that it does not necessarily represent a running computation. Instead, we reintroduce nondeterminism explicitly using the functions of thescalaz.Nondeterminsm
interface. This simplifies our implementation and makes code easier to reason about, since the order of effects and the points of nondeterminism are made fully explicit and do not depend on Scala's evaluation order.IMPORTANT NOTE:
Future
does not include any error handling and should generally only be used as a building block by library writers who want to build onFuture
's capabilities but wish to design their own error handling strategy. Seescalaz.concurrent.Task
for a type that extendsFuture
with proper error handling -- it is merely a wrapper forFuture[Either[Throwable,A]]
with a number of additional convenience functions.
来源:https://stackoverflow.com/questions/19794714/when-using-scala-futures-will-chained-callbacks-with-the-same-execution-context