web-dev-qa-db-fra.com

Exécution de la fonction de retard

Quelle est la manière la plus simple de retarder l'exécution d'une fonction dans Scala, quelque chose comme setTimeout de JavaScript? Idéalement sans générer de thread par exécution retardée, c'est-à-dire exécution séquentielle. Le plus proche que j'ai pu trouver était celui d'Akka Scheduler , mais c'est une exagération.

À des fins de test, j'ouvre des milliers de connexions, puis elles obtiennent des réponses en 10 secondes. Dans node.js, cela ressemble à:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  setTimeout(function() {res.end('Hello World\n');}, 10000 );
}).listen(8080, '127.0.0.1');

Mais quelle serait la version la plus proche Scala de faire la même chose? Peu m'importe si res.end va être exécuté dans plusieurs threads ou mis en file d'attente dans un seul.

19
Oleg Mikheev

Fatigué de se flak pour répondre à la question pour le plus simple aussi, voici les idiomes JVM standard:

$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
Type in expressions for evaluation. Or try :help.

scala> import Java.util.{Timer,TimerTask}
import Java.util.{Timer, TimerTask}

scala> val timer = new Timer
timer: Java.util.Timer = Java.util.Timer@2d9ffd6f

scala> def delay(f: () => Unit, n: Long) = timer.schedule(new TimerTask() { def run = f() }, n)
delay: (f: () => Unit, n: Long)Unit

scala> delay(() => println("Done"), 1000L)

scala> Done


scala> import Java.util.concurrent._
import Java.util.concurrent._

scala> val x = Executors.newScheduledThreadPool(2)
x: Java.util.concurrent.ScheduledExecutorService = Java.util.concurrent.ScheduledThreadPoolExecutor@2c5d529e

scala> x.schedule(new Callable[Int]() { def call = { println("Ran"); 42 }}, 1L, TimeUnit.SECONDS)
res3: Java.util.concurrent.ScheduledFuture[Int] = Java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@3ab0f534

scala> Ran

Il n'y a pas d'API pour planifier une tâche retardée dans la bibliothèque standard, mais vous pouvez créer un ExecutionContext avec un délai fixe, afin d'utiliser Scala Future .

scala> import scala.concurrent._
import scala.concurrent._

scala> implicit val xx = new ExecutionContext() {
     | def reportFailure(t: Throwable) = t.printStackTrace()
     | def execute(r: Runnable) = x.schedule(new Callable[Unit]() { def call = r.run() }, 1L, TimeUnit.SECONDS)
     | }
xx: scala.concurrent.ExecutionContext = $anon$1@40d3ab8b

scala> Future(println("hello"))
res4: scala.concurrent.Future[Unit] = List()

scala> hello

scala> Future(42)
res5: scala.concurrent.Future[Int] = List()                

scala> .value
res6: Option[scala.util.Try[Int]] = Some(Success(42))

Ou vous pouvez utiliser le planificateur d'Akka, qui est la réponse canonique de Scheduled Executor in Scala

L'ancien monoplace:

Le plus simple est toujours juste future { blocking(Thread.sleep(10000L)); "done" }

mais je voulais placer une annonce pour ce type, que je viens de rencontrer, qui vous donne un indicateur de progression ou une valeur intermédiaire. J'aurais aimé qu'il ait un nom différent, c'est tout.

scala> import concurrent._
import concurrent._

scala> import ExecutionContext.Implicits._
import ExecutionContext.Implicits._

scala> import duration._
import duration._

scala> val deadline = 60.seconds.fromNow
deadline: scala.concurrent.duration.Deadline = Deadline(38794983852399 nanoseconds)

scala> new DelayedLazyVal(() => deadline.timeLeft.max(Duration.Zero), blocking {
     | Thread.sleep(deadline.timeLeft.toMillis)
     | Console println "Working!"
     | })
res9: scala.concurrent.DelayedLazyVal[scala.concurrent.duration.FiniteDuration] = scala.concurrent.DelayedLazyVal@50b56ef3

scala> res9()
res10: scala.concurrent.duration.FiniteDuration = 23137149130 nanoseconds

scala> res9.isDone
res11: Boolean = false

scala> res9()
res12: scala.concurrent.duration.FiniteDuration = 12499910694 nanoseconds

scala> res9()
res13: scala.concurrent.duration.FiniteDuration = 5232807506 nanoseconds

scala> Working!


scala> res9.isDone
res14: Boolean = true

scala> res9()
res15: scala.concurrent.duration.FiniteDuration = 0 days

Voici une formulation alternative avec Soit, pour calculer une valeur après un délai. Utiliser Left bien sûr quand il est encore temps Left.

scala> new DelayedLazyVal(()=> if (deadline.hasTimeLeft) Left(deadline.timeLeft) else
     | Right("Working!"), blocking(Thread.sleep(deadline.timeLeft.toMillis)))
res21: scala.concurrent.DelayedLazyVal[Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String]] = scala.concurrent.DelayedLazyVal@78f9c6f2

scala> res21()
res22: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Left(28553649064 nanoseconds)

scala> res21()
res23: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Left(9378334087 nanoseconds)

scala> res21.isDone
res24: Boolean = false

scala> res21()
res25: Product with Serializable with scala.util.Either[scala.concurrent.duration.FiniteDuration,String] = Right(Working!)

scala> res21.isDone
res26: Boolean = true
21
som-snytt