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?
Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.
Whenever we create a new Future operation, Scala spawns a new thread to run that Future's code, and after completion it executes any provided callbacks. Scala will infer that add has a return type of Future[Int] , and the enclosed code will execute in its own thread when the function is called.
An ExecutionContext can execute program logic asynchronously, typically but not necessarily on a thread pool. A general purpose ExecutionContext must be asynchronous in executing any Runnable that is passed into its execute -method.
The Promise is a writable, single-assignment container that completes a Future. The Promise is similar to the Future. However, the Future is about the read-side of an asynchronous operation, while the Promise is about the write-side.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With