可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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
回答3:
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:
If you want schedule the completion without Akka, you can use a regular Java Timer to schedule a promise to complete:
def delay[T](delay: Long)(block: => T): Future[T] = {
val promise = Promise[T]()
val t = new Timer()
t.schedule(new TimerTask {
override def run(): Unit = {
promise.complete(Try(block))
}
}, delay)
promise.future
}
回答5:
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
}
回答6:
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:
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()
}