val sayHello = Future {
Thread.sleep(1000)
"hello"
}
val f : Future = ???
((f map f1) map f2) map f3
f map (f1∘f2 ∘f3)
fut.map(g)
requires an ExecutionContext for the same reason Future.apply
does: to specify where g
will run once fut
completes.Just because you have an existing Future, doesn’t mean you have the ExecutionContext where it executed; there may not even have been one to begin with. For instance, Future.successful(1)
creates a completed Future with the value 1. (Advice: look at the definition of that method.) And a Promise creates a Future without executing any code.
When you map
on such a future, where and when will your function run? You have to provide an ExecutionContext to answer that question.
However, if you do have a blocking piece of code and you’re going to run it in a Future, then map
isn’t any different from Future.apply
.
(f map f1) map f2
and f map (f1*f2)
. How large it is depends on what you care about. It’s a very small cost in throughput, but it might be a large cost in latency, depending on how many Futures are waiting to be executed. If f1 and f2 are both synchronous functions (i.e. they’re not returning a Future), there’s no reason to call map twice.
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Hi Ezequiel,
On Apr 26, 2016 23:19, "Ezequiel Surijon" <sur...@gmail.com> wrote:
>
> Futures are used for example to avoid blocking the current thread, and let the blocking code to run in a different thread. When using Async framwork like PlayFramework, you are supossed to execute any blocking code inside a Future
>
> When you create a future like this:
>
> val sayHello = Future {
> Thread.sleep(1000)
> "hello"
> }
>
>
> An implicit ExecutionContext is passed to:
>
> Future.apply[T](body: ⇒ T)(implicit executor: ExecutionContext): Future[T]
>
> And it makes sense, since you have to specify where you code block will be executed.
>
> But I can't understand why map a future requires an execution context.
>
> Future.map[S](f: (T) ⇒ S)(implicit executor: ExecutionContext): Future[S]
>
> As far I undestand the mapping function f, will be executed once the future is resolved, i.e. after the blocking code was executed, and I think it should be executed and f also is supposed to be a non blocking code block, if not you better to use flatMap
>
> So my questions are
>
> Why Future.map requires an execution context?
Because there is what's called a "race condition" between the future being completed and the mapping function being registered as a callback.
This means that there are 2 outcomes:
The thread which completes the future also is responsible for executing all existing callbacks.
The thread which adds the callback has to execute the callback since the future has already been completed.
This means that it is no longer possible to reason about what executes where and when. My dear, and former, colleague Havoc Pennington did an excellent writeup on this very problem here: http://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/
As an aside, Future.apply is only syntactic bacon over Future.successful(()).map(_ => block)
Thanks for raising these questions!
>
> There is a penalty to map a future several times?
Yes. But it is a classic tradeoff in fairness and throughput.
There is a difference in performace thread compsumtion in bellow code?
>
> val f : Future = ???
> ((f map f1) map f2) map f3
>
> f map (f1∘f2 ∘f3)
>
>
>
Using map
many times has only two possible shortcomings: performance, and code readability.
Performance penalties, like always, should be demonstrated before being optimized: premature optimization is the root of all evil.
To improve readability, consider using scala-async. Some people also use for comprehensions (i.e. for (result <- future) yield ...
).
Of course, when composing synchronous functions (that don’t return Future), you should always compose them directly - it’s easier, faster, and more readable.
Daniel Armak
Using
map
many times has only two possible shortcomings: performance, and code readability.