Reputation: 802
Why is the user's name not printed when I comment out the println("testing")
?
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object Future3 extends App {
val userFuture = Future(
User("Me")
)
val userNameFuture: Future[String] = userFuture map {
user => user.name
}
userNameFuture onSuccess {
case userName => println(s"user's name = $userName")
}
// println("testing")
}
case class User(name: String)
Upvotes: 2
Views: 129
Reputation: 6059
The ExecutionContext.Implicits.global
creates daemon threads. (see Scala source code scala.concurrent.impl.ExecutionContextImpl.DefaultThreadFactory
) These are threads the JVM will not wait for on exit (in your case, when the main routine stops). Thus, before the userNameFuture
which runs as a daemon thread has finished, the main routine is already finished and does not wait for the future threads to finish.
To prevent this from happening, either use non-daemon thread, e.g. to create such an implicit ExecutionContext
implicit val ec = (scala.concurrent.ExecutionContext.fromExecutorService(Executors.newCachedThreadPool()))
or use
Await.result( userNameFuture, Duration.Inf )
in the main routine.
Attention: If you use the latter approach with both Await.result
and onSuccess
callback, it still can happen, that the main routine exits first and no output of username will be made, as there is no order which of both happens first.
Have a look at the code
object F2 {
def main(args: Array[String]): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
val userFuture = Future {
Thread.sleep(1000)
println( "userFuture runs on: " + Thread.currentThread().getName)
Thread.sleep(1000)
User("Me")
}
val userNameFuture: Future[String] = userFuture map {
user => {
Thread.sleep(2000)
println( "map runs on: " + Thread.currentThread().getName )
Thread.sleep(2000)
user.name
}
}
val p = Promise[Boolean]()
userNameFuture onSuccess {
case userName => {
println( "onSuccess runs on : " + Thread.currentThread().getName )
println(s"user's name = $userName")
p.complete(Success(true))
}
}
println( "main runs on: " + Thread.currentThread().getName )
println( "main is waiting (for promise to complete) .... ")
Await.result( p.future, Duration.Inf )
println( "main got promise fulfilled")
println( "main end ")
}
}
whose output is
main runs on: run-main-b
main is waiting (for promise to complete) ....
userFuture runs on: ForkJoinPool-1-worker-5
map runs on: ForkJoinPool-1-worker-5
onSuccess runs on : ForkJoinPool-1-worker-5
user's name = Me
main got promise fulfilled
main end
First, you can see, that both userFuture and it's map operation run on ForkJoinPool
as daemon threads.
Second, main is run through first, printing "main is waiting for promise" and waits here (only for demenstration purposes) for the promise to be fulfilled. If main wouldn't wait here ( try yourself, by commenting out the Await
) for the promise completion, the main routine would just print the other two lines and is done. As a result, the JVM would close (and you would never see output of onComplete
)
In general, if you are using SBT and invoke program execution via run
, then you can still see the output of daemon threads, as the JVM is not terminated, if started from within SBT.
So, if you start via SBT run
you are soon back to the SBT prompt (because main routine has finished), but output of threads (onComplete
) is visible in SBT.
Upvotes: 0
Reputation: 5424
The reason is that default ExecutionContext
global
executes your future block on daemon thread and main thread doesn't wait for daemons to complete. You can use Thread.sleep(1000)
, Await.result(userNameFuture, 1 second)
or another thread blocking operation in main thread to wait for some time so that your future's thread completes.
Another way is to run future on not-daemon thread:
import java.util.concurrent.Executors
import scala.concurrent.{ExecutionContext, Future}
object Future3 extends App {
implicit val executor = ExecutionContext
.fromExecutorService(Executors.newCachedThreadPool()) //not-daemon threads
val userFuture = Future(
User("Me")
)
val userNameFuture: Future[String] = userFuture map {
user => user.name
}
userNameFuture onSuccess {
case userName => println(s"user's name = $userName")
}
}
case class User(name: String)
Upvotes: 2