Michael
Michael

Reputation: 42050

How to implement a delayed future without java.util.Timer?

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

Answers (2)

Dima
Dima

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

Oleg Pyzhcov
Oleg Pyzhcov

Reputation: 7353

You can use one of Java's ScheduledExecutorServices, 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

Related Questions