J'essaie de mettre en œuvre l'avenir prévu à Scala. Je voudrais qu'il attende un moment précis et exécute ensuite le corps. Jusqu'à présent, j'ai essayé l'approche simple et suivante
val d = 5.seconds.fromNow
val f = future {Await.ready(Promise().future, d.timeLeft); 1}
val res = Await.result(f, Duration.Inf)
mais je reçois le TimeoutExcpetion sur l'avenir. Est-ce même la bonne approche ou dois-je simplement utiliser ScheduledExecutor de Java?
Vous pouvez changer votre code en quelque chose comme ceci:
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))
}
Mais je ne le recommanderais pas. Ce faisant, vous bloqueriez dans un avenir (bloquer pour attendre ce Promise
qui ne se terminera jamais), et je pense que le blocage dans le ExecutionContext
est fortement déconseillé. Je chercherais à utiliser l'exécuteur planifié Java comme vous l'avez indiqué) ou vous pourriez envisager d'utiliser Akka comme @ alex23 recommandé.
Akka a akka.pattern:
def after[T](duration: FiniteDuration, using: Scheduler)(value: ⇒ Future[T])(implicit ec: ExecutionContext): Future[T]
"Renvoie un scala.concurrent.Future qui sera terminé avec le succès ou l'échec de la valeur fournie après la durée spécifiée."
Il n'y a rien à faire dès le départ en utilisant uniquement la bibliothèque standard. Pour la plupart des cas d'utilisation simples, vous pouvez utiliser un petit assistant tel que celui-ci:
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 ) )
}
}
Cela peut être utilisé comme ceci:
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._
DelayedFuture( 5 seconds )( println("Hello") )
Notez que contrairement à Java futures planifiés, cette implémentation ne vous permettra pas d'annuler le futur.
Si vous souhaitez planifier l'achèvement sans Akka, vous pouvez utiliser un temporisateur Java Timer pour planifier une promesse de terminer:
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
}
Ma solution est assez similaire à celle de Régis mais j'utilise Akka pour programmer:
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
}
Toutes les autres solutions utilisent akka ou bloquent un thread par tâche retardée. Une meilleure solution (sauf si vous utilisez déjà akka) consiste à utiliser ScheduledThreadPoolExecutor de Java. Voici un exemple d'un wrapper scala pour cela: