I have a future(doFour
) that is executed and results passed to a flatmap.
Inside the flatmap I execute two more future(doOne and doTwo) functions expecting them to run on parallel but I see they are running sequentially (2.13). Scastie
Why are doOne
and doTwo
not execute in parallel ?
How can I have them to run in parallel ?
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
object Test {
def doOne(): Future[Unit] = Future {
println("startFirst"); Thread.sleep(3000); println("stopFirst")
}
def doTwo(): Future[Unit] = Future {
println("startSecond"); Thread.sleep(1000); println("stopSecond")
}
def doFour(): Future[Unit] = Future {
println("do 4"); Thread.sleep(1000); println("done 4")
}
def main(args: Array[String]) {
val resOut = doFour().flatMap { a =>
val futureOperations = Seq(doOne(), doTwo())
val res = Future.sequence(futureOperations)
res
}
val stream = Await.result(resOut, Duration.Inf)
}
}
A Future
becomes eligible for execution as soon as it is created. So this line creates two Futures
that can potentially be executed:
val futureOperations = Seq(doOne(), doTwo())
The call to Future.sequence
will create a new Future
that waits for each of the futures to complete in turn, but they will both already be available for execution by this point in the code.
val res = Future.sequence(futureOperations)
If you want Future
s to start sequentially you need to use map/flatMap
:
val res = doOne().map( _ => doTwo())
With this code doTwo
will not be called until doOne
completes (and not at all if doOne
fails)
The reason that this does not appear to happen in your example is that you are calling a blocking operation in your Future
which is blocking a thread that would otherwise be used to execute other Future
s. So although there are two Future
s available for execution, only one is actually being executed at a time.
If you mark the code as blocking
it works correctly:
import scala.concurrent.blocking
def doOne(): Future[Unit] = Future {
blocking{println("startFirst"); Thread.sleep(3000); println("stop First")}
}
def doTwo(): Future[Unit] = Future {
blocking{println("startSecond"); Thread.sleep(1000); println("stop Second")}
}
See the comments section for details of why the default behaviour is different on different versions, and why you should never make assumptions about the relative execution order of independent Future
s.