Scala - ScheduledFuture

2019-01-21 19:26发布

I am trying to implement scheduled future in Scala. I would like it to wait specific time and then execute the body. So far I tried the following, simple approach

val d = 5.seconds.fromNow

val f = future {Await.ready(Promise().future, d.timeLeft); 1}

val res = Await.result(f, Duration.Inf)

but I am getting the TimeoutExcpetion on the future. Is this even the correct approach or should I simply use the ScheduledExecutor from Java?

标签: scala future
7条回答
姐就是有狂的资本
2楼-- · 2019-01-21 19:55

Shortest solution for this, is probably making use of scala-async:

import scala.async.Async.{async, await}

def delay[T](value: T, t: duration): Future[T] = async {
  Thread.sleep(t.toMillis)
  value
}

Or in case you want delayed execution of a block

def delay[T](t: duration)(block: => T): Future[T] async {
  Thread.sleep(t.toMillis)
  block()
}
查看更多
甜甜的少女心
3楼-- · 2019-01-21 19:59

There is nothing to do that out of the box using the standard library alone. For most simple use cases, you can use a little helper such as this:

object DelayedFuture {
  import java.util.{Timer, TimerTask}
  import java.util.Date
  import scala.concurrent._
  import scala.concurrent.duration.FiniteDuration
  import scala.util.Try

  private val timer = new Timer(true)

  private def makeTask[T]( body: => T )( schedule: TimerTask => Unit )(implicit ctx: ExecutionContext): Future[T] = {
    val prom = Promise[T]()
    schedule(
      new TimerTask{
        def run() {
          // IMPORTANT: The timer task just starts the execution on the passed
          // ExecutionContext and is thus almost instantaneous (making it 
          // practical to use a single  Timer - hence a single background thread).
          ctx.execute( 
            new Runnable {
              def run() {
                prom.complete(Try(body))
              }
            }
          )
        }
      }
    )
    prom.future
  }
  def apply[T]( delay: Long )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
    makeTask( body )( timer.schedule( _, delay ) )
  }
  def apply[T]( date: Date )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
    makeTask( body )( timer.schedule( _, date ) )
  }
  def apply[T]( delay: FiniteDuration )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
    makeTask( body )( timer.schedule( _, delay.toMillis ) )
  }
}

This can be used like this:

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._

DelayedFuture( 5 seconds )( println("Hello") )

Note that unlike java scheduled futures, this implementation will not let you cancel the future.

查看更多
手持菜刀,她持情操
4楼-- · 2019-01-21 20:00

Akka has akka.pattern:

def after[T](duration: FiniteDuration, using: Scheduler)(value: ⇒ Future[T])(implicit ec: ExecutionContext): Future[T]

"Returns a scala.concurrent.Future that will be completed with the success or failure of the provided value after the specified duration."

http://doc.akka.io/api/akka/2.2.1/#akka.pattern.package

查看更多
戒情不戒烟
5楼-- · 2019-01-21 20:03

My solution is pretty similar to Régis's but I use Akka to schedule:

 def delayedFuture[T](delay: FiniteDuration)(block: => T)(implicit executor : ExecutionContext): Future[T] = {
    val promise = Promise[T]

    Akka.system.scheduler.scheduleOnce(delay) {
      try {
        val result = block
        promise.complete(Success(result))
      } catch {
        case t: Throwable => promise.failure(t)
      }
    }
    promise.future
  }
查看更多
Root(大扎)
6楼-- · 2019-01-21 20:05

All the other solutions use either akka or block a thread per delayed task. A better solution (unless you are already using akka) is to use java's ScheduledThreadPoolExecutor. Here's an example of a scala wrapper for that:

https://gist.github.com/platy/8f0e634c64d9fb54559c

查看更多
对你真心纯属浪费
7楼-- · 2019-01-21 20:12

You could change your code to something like this:

val d = 5.seconds.fromNow
val f = Future {delay(d); 1}
val res = Await.result(f, Duration.Inf)

def delay(dur:Deadline) = {
  Try(Await.ready(Promise().future, dur.timeLeft))
}

But I would not recommend it. In doing so, you would be blocking in a Future (blocking to wait for that Promise that will never complete), and I think blocking in the ExecutionContext is greatly discouraged. I would either look into using the java scheduled executor as you stated or you could look into using Akka as @alex23 recommended.

查看更多
登录 后发表回答