Scala - ЗапланированоFuture
Я пытаюсь реализовать запланированное будущее в Scala. Я бы хотел, чтобы он подождал определенное время, а затем исполнил тело. До сих пор я пробовал следующий, простой подход
val d = 5.seconds.fromNow
val f = future {Await.ready(Promise().future, d.timeLeft); 1}
val res = Await.result(f, Duration.Inf)
но я получаю TimeoutExcpetion в будущем. Это даже правильный подход или я просто должен использовать ScheduledExecutor из Java?
Ответы
Ответ 1
Вы можете изменить свой код на что-то вроде этого:
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))
}
Но я бы не рекомендовал его. При этом вы блокируете в Будущем (блокировка, чтобы ждать этого Promise
, который никогда не будет завершен), и я думаю, что блокировка в ExecutionContext
сильно не рекомендуется. Я бы либо изучил использование запланированного исполнителя java, как вы заявили, либо вы можете изучить использование Akka как рекомендуется @alex23.
Ответ 2
Акка имеет akka.pattern:
def after[T](duration: FiniteDuration, using: Scheduler)(value: ⇒ Future[T])(implicit ec: ExecutionContext): Future[T]
"Возвращает scala.concurrent.Future, который будет завершен с успехом или неудачей предоставленного значения после указанной продолжительности."
http://doc.akka.io/api/akka/2.2.1/#akka.pattern.package
Ответ 3
Нет ничего, что можно было бы сделать из коробки, используя только стандартную библиотеку.
Для большинства простых случаев использования вы можете использовать небольшой помощник, например:
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 ) )
}
}
Это можно использовать следующим образом:
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._
DelayedFuture( 5 seconds )( println("Hello") )
Обратите внимание, что в отличие от фьючерсов java, эта реализация не позволит вам отменить будущее.
Ответ 4
Если вы хотите запланировать завершение без Akka, вы можете использовать обычный Java-таймер для планирования обещания:
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
Мое решение очень похоже на Régis, но я использую Akka для расписания:
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
Все остальные решения используют либо akka, либо блокируют поток за одну задержку. Лучшим решением (если вы уже используете akka) является использование java ScheduledThreadPoolExecutor. Вот пример обертки scala для этого:
https://gist.github.com/platy/8f0e634c64d9fb54559c
Ответ 7
Самое короткое решение для этого, вероятно, использует scala -async:
import scala.async.Async.{async, await}
def delay[T](value: T, t: duration): Future[T] = async {
Thread.sleep(t.toMillis)
value
}
Или, если вы хотите выполнить отложенное выполнение блока
def delay[T](t: duration)(block: => T): Future[T] async {
Thread.sleep(t.toMillis)
block()
}