How to poll with a Future in Scala?
It is actually not recursive at all, so the stack will be fine.
One improvement to your approach I can think of is to use some sort of scheduler instead of Thread.sleep
so that you don't hold up the thread.
This example uses standard java's TimerTask
, but if you are using some kind of a framework, like akka, play or whatever, it probably has its own scheduler, that would be a better alternative.
object Scheduler {
val timer = new Timer(true)
def after[T](d: Duration)(f :=> Future[T]): Future[T] = {
val promise = Promise[T]()
timer.schedule(TimerTask { def run() = promise.completeWith(f) }, d.toMillis)
promise.future
}
}
def untilComplete(attempts: Int = 10) = isComplete().flatMap {
case true => Future.successful(())
case false if attempts > 1 => Scheduler.after(100 millis)(untilComplete(attempts-1))
case _ => throw new Exception("Attempts exhausted.")
}
You could use Akka Streams. For example, to call isComplete
every 500 milliseconds until the result of the Future
is true, up to a maximum of five times:
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{ Sink, Source }
import scala.concurrent.Future
import scala.concurrent.duration._
def isComplete(): Future[Boolean] = ???
implicit val system = ActorSystem("MyExample")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val stream: Future[Option[Boolean]] =
Source(1 to 5)
.throttle(1, 500 millis)
.mapAsync(parallelism = 1)(_ => isComplete())
.takeWhile(_ == false, true)
.runWith(Sink.lastOption)
stream onComplete { result =>
println(s"Stream completed with result: $result")
system.terminate()
}