Reputation: 42050
I am writing a simple function to return a Future[Unit]
that completes after a given delay.
def delayedFuture(delay: FiniteDuration): Future[Unit] = {
val promise = Promise[Unit]()
val timerTask = new java.util.TimerTask {
override def run(): Unit = promise.complete(Success(()))
}
new java.util.Timer().schedule(timerTask, delay.toMillis)
promise.future
}
This implementation can probably work but I don't like creating a new Timer
for each invocation because each Timer
instance creates a thread. I can pass a Timer
instance to delayedFuture
as an argument but I don't want client code to know about Timer
. So I guess I cannot use java.util.Timer
at all.
I would like to use ExecutionContext
for task scheduling and maybe define delayedFuture
like this:
def delayedFuture(delay: FiniteDuration)
(implicit ec: ExecutoionContext): Future[Unit] = ???
What is the simplest way to implement delayedFuture
like this without java.util.Timer
?
Upvotes: 1
Views: 1207
Reputation: 40500
You don't need to create a new Timer
for each invocation. Just make one global one.
object Delayed {
private val timer = new Timer
def apply[T](delay: Duration)(task: => T): Future[T] = {
val promise = Promise[T]()
val tt = new TimerTask {
override def run(): Unit = promise.success(task)
}
timer.schedule(tt, delay.toMillis)
promise.future
}
def unit(delay: Duration) = apply[Unit](delay) { () }
}
Then, Delayed.unit(10 seconds)
gives you a future unit that satisfies in 10 seconds.
Upvotes: 3
Reputation: 7353
You can use one of Java's ScheduledExecutorService
s, e.g. create a single-thread pool and use it for scheduling only:
val svc = new ScheduledThreadPoolExecutor(1, (r: Runnable) => {
val t = new Thread(r)
t.setName("future-delayer")
t.setDaemon(true) // don't hog the app with this thread existing
t
})
def delayedFuture(d: FiniteDuration) = {
val p = Promise[Unit]()
svc.schedule(() => p.success(()), d.length, d.unit)
p.future
}
Upvotes: 3