It’s mainly the general concept of Futures plus #map()
/#flatMap()
/for
-expressions (extending beyond futures and likely not intuitive when encountered for the first time). The basics are covered in the starter’s book, but it’s certainly advisable to go with a more in-depth resource.
The code you posted kind of works, but just kind of. The first problem is that your main thread will just exit after it has spawned the initial future. If at the end you add some code that keeps your main thread running infinitely, like
Await.ready(Future.never, Duration.Inf)
…you’ll actually see the watcher happily reacting to events. You don’t have an abstraction/handle for the chain of futures that comprise the loop, though - each #onComplete()
handler will just fire up a new, unrelated future. In order to represent this sequential chain as an aggregate future instance of its own, you can combine the futures via #flatMap()
instead. Example:
object FutureLoop extends App {
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
def loopStep(start: Int): Future[Int] =
Future {
println(start)
blocking { Thread.sleep(1000) }
start + 1
}
def loopFlatMap(start: Int, max: Int): Future[Unit] =
loopStep(start).flatMap { next=>
if(next > max) Future.successful(()) else loopFlatMap(next, max)
}
Await.ready(loopFlatMap(1, 10), Duration.Inf)
}
This way, you’ll have a future instance that represents the complete loop - you can wait for it in your #main()
and/or combine it with other futures.
Instead of using #flatMap()
directly, you could equivalently express this using a for
-expression.
def loopFor(start: Int, max: Int): Future[Unit] =
for {
next <- loopStep(start)
res <- if(next > max) Future.successful(()) else loopFor(next, max)
} yield res
Await.ready(loopFor(1, 10), Duration.Inf)
The Watcher
example I’ve posted before uses exactly the same structure. The main difference is that I’ve further split up the “loop body” (i.e. #watchStep()
) into substeps. I’d hoped for this to show how for
-expressions can be used to express multiple nested #flatMap()
invocations more conveniently like a linear sequence of calls.