Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When using Scala futures, will chained callbacks with the same execution context be optimised into synchronous calls?

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?

like image 568
SoftMemes Avatar asked Nov 05 '13 17:11

SoftMemes


People also ask

How do Scala Futures work?

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.

How do you handle Future Scala?

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.

What is execution context in Scala?

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.

What are Futures and promises in Scala?

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.


1 Answers

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 and flatMap do NOT spawn new tasks and do not require an implicit ExecutionContext. Instead, map and flatMap merely add to the current (trampolined) continuation that will be run by the 'current' thread, unless explicitly forked via Future.fork or Future.apply. This means that Future achieves much better thread reuse than the 2.10 implementation and avoids needless thread pool submit cycles.

Future also differs from the scala 2.10 Future type in that it does not necessarily represent a running computation. Instead, we reintroduce nondeterminism explicitly using the functions of the scalaz.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 on Future's capabilities but wish to design their own error handling strategy. See scalaz.concurrent.Task for a type that extends Future with proper error handling -- it is merely a wrapper for Future[Either[Throwable,A]] with a number of additional convenience functions.

like image 69
Piotr Kukielka Avatar answered Sep 21 '22 09:09

Piotr Kukielka